Introduction

Outline

In this class I will

  • Briefly outline the history of R.

  • Using some examples briefly show how to do data wrangling and visualize data in R.

Materials for this class are available on GitHub at https://github.com/ltierney/SIBS-WV-2021.git.

  • You can access it as an RStudio project by following the menu selection File > New Project > Version Control > Git and specifying this URL.

  • You can use the git command line client with

    git clone https://github.com/ltierney/SIBS-WV-2021.git

Materials for our Data Visualization and Data Technologies course are available at

http://www.stat.uiowa.edu/~luke/classes/STAT4580-2021/

Tools

Some tools I will be using:

  • The RStudio IDE.

  • Many features from the basic R distribution.

  • Some tools from the tidyverse.

  • The ggplot package based on the Grammar of Graphics framework.

Most of the packages are loaded by loading the tidyverse package:

library(tidyverse)

References

Useful references:

Hadley Wickham and Garrett Grolemund (2016), R for Data Science, O'Reilly.

Claus O. Wilke (2019), Fundamentals of Data Visualization, O'Reilly.

Kieran Healy (2018) Data Visualization: A practical introduction, Princeton

Rafael A. Irizarry (2019), Introduction to Data Science: Data Analysis and Prediction Algorithms with R, Chapman & Hall/CRC. (Book source on GitHub)

Ask questions any time!

The R Language

Background

R is a language for data analysis and graphics.

  • R was originally developed by Robert Gentleman and Ross Ihaka in the early 1990's for a Macintosh computer lab at U. of Auckland, New Zealand.

  • R is based on the S language developed by John Chambers and others at Bell Labs.

R is an Open Source project.

  • Since 1997 R is developed and maintained by the R-core group, with around 20 members located in maor than 10 different countries.

  • R is widely used in the field of statistics and beyond, especially in university environments.

  • R has become the primary framework for developing and making available new statistical methodology.

  • Many (now over 17,000) extension packages are available through CRAN or similar repositories.

Working with R

R is designed for interactive data exploration.

  • Interaction is through a read-eval-print loop (REPL).

  • This is also called a command line interface (CLI).

All computations are specified in the R language.

  • Even for simple tasks you need to know a little of the language.

  • After learning to do simple tasks you know some of the language.

The language is used to

  • prepare data for analysis;

  • specify individual analyses;

  • program repeated or similar analyses;

  • program new methods of analysis.

Specifying these tasks in a language supports reproducible research.

The R language operates on vectors and arrays.

Commonly used data types are:

  • integer and numeric vectors;

  • logical vectors;

  • character vectors;

  • factors.

All basic vector types support missing (NA) values.

Arithmetic operations are vectorized to operate element-wise on vectors.

Data vectors are usually combined into table-like objects called data frames.

The Data Analysis Process

A figure that shows the steps usually involved in a data analysis project:

These steps are often repeated many times, so it is important to make your work reproducible.

Reproducible Data Analysis

Making your work reproducible:

  • Save you work in a text file or notebook.

  • Track changes to your files with a version control system like git.

  • Use a system like Rmarkdown to prepare your reports.

This allows you to re-create your report when data changes (as it often will!).

A good resource for setting up your tools to support this is Happy Git and GitHub for the useR.

Some Examples

Example Data Sets

When working with research data, a first step is usually to read and clean the data.

We'll put that off for a little while and work with some data sets made available in R packages.

Data sets available in R packages include:

  • many classic data sets;

  • newer, often larger, data sets useful for learning;

  • current data obtained by querying web APIs.

Old Faithful Eruptions

A simple classic data set is the geyser data frame available in package MASS.

data(geyser, package = "MASS")
dim(geyser)
## [1] 299   2
head(geyser, 4)
##   waiting duration
## 1      80 4.016667
## 2      71 2.150000
## 3      57 4.000000
## 4      80 4.000000

head and tail return the first and last few rows of a data frame.

They are useful for quick sanity checks.

The rows represent measurements recorded for eruptions of the Old Faithful geyser in Yellowstone National Park, Wyoming.

The variables are:

  • waiting: the time in minutes since the previous eruption;

  • duration: the duration in minutes of the eruption.

The durations have a bimodal distribution:

ggplot(geyser) +
    geom_histogram(aes(x = duration),
                   bins = 15,
                   color = "black",
                   fill = "grey")

This is ggplot code for creating a histogram.

A basic template for creating a plot with ggplot:

ggplot(data = <DATA>) +
    <GEOM>(mapping = aes(<MAPPINGS>))

An interesting question is whether the duration can be used to predict when the next eruption will occur.

A plot of the previous duration against the waiting time to the current eruption:

ggplot(geyser) +
    geom_point(aes(x = lag(duration),
                   y = waiting))

It looks like a useful rule would be to expect a shorter waiting time after a shorter eruption duration.

An interesting feature:

Many durations are recorded as 2 or 4 minutes.

This can also be seen in a histogram with a small bin width:

p <- ggplot(geyser) +
    geom_histogram(aes(x = duration,
                       y = stat(density)),
                   fill = "grey",
                   color = "black",
                   binwidth = 0.1)
p

ggplot produces a plot object.

Drawing only happens when the object is printed.

Does this rounding matter?

  • For many analyses it probably doesn't.

  • It might if you wanted to fit normal distributions to the two groups.

Taking 3 minutes as the divide between short and long durations we can first pick out the short and long durations:

d <- geyser$duration
d_short <- d[d < 3]
d_long <- d[d >= 3]

Then compute the means and standard deviations as

mean(d_short)
## [1] 1.980317
sd(d_short)
## [1] 0.2779829
mean(d_long)
## [1] 4.262113
sd(d_long)
## [1] 0.3937525
mean(d >= 3)
## [1] 0.6488294

An approach that scales better:

Compute group summaries using tools from the dplyr tidyverse package.

First, add a type variable:

geyser <- mutate(geyser, type = ifelse(duration < 3, "short", "long"))

The summaries can then be computed as

sgd <- summarize(group_by(geyser, type),
                 mean = mean(duration),
                 sd = sd(duration),
                 n = n())
(sgd <- mutate(sgd, prop = n / sum(n)))
## # A tibble: 2 x 5
##   type   mean    sd     n  prop
##   <chr> <dbl> <dbl> <int> <dbl>
## 1 long   4.26 0.394   194 0.649
## 2 short  1.98 0.278   105 0.351

summarize, group_by, and mutate are from the dplyr package that implements a grammar of data manipulation.

This computation can also be written using the forward pipe operator %>%:

sgd <-
    group_by(geyser, type) %>%
    summarize(mean = mean(duration),
              sd = sd(duration),
              n = n()) %>%
    ungroup() %>%
    mutate(prop = n / sum(n))
sgd
## # A tibble: 2 x 5
##   type   mean    sd     n  prop
##   <chr> <dbl> <dbl> <int> <dbl>
## 1 long   4.26 0.394   194 0.649
## 2 short  1.98 0.278   105 0.351

The pipe operator allows a sequence of operations to be chained together.

The left-hand operation is passed implicitly as the first argument to the function called on the right.

One way to show the superimposed normal densities:

f1 <- function(x)
    sgd$prop[1] * dnorm(x, sgd$mean[1], sgd$sd[1])
f2 <- function(x)
    sgd$prop[2] * dnorm(x, sgd$mean[2], sgd$sd[2])
p <- p +
    stat_function(color = "red", fun = f1) +
    stat_function(color = "blue", fun = f2)
p

A ggplot can consist of several layers.

The means and standard deviations are affected by the rounding.

Summaries that omit values equal to 2 or 4 minutes can be computed as

geyser2 <- filter(geyser, duration != 2, duration != 4)
sgd2 <-
    group_by(geyser2, type) %>%
    summarize(mean = mean(duration),
              sd = sd(duration),
              n = n()) %>%
    ungroup() %>%
    mutate(prop = n / sum(n))
sgd2
## # A tibble: 2 x 5
##   type   mean    sd     n  prop
##   <chr> <dbl> <dbl> <int> <dbl>
## 1 long   4.36 0.422   141 0.632
## 2 short  1.97 0.315    82 0.368

A plot showing curves computed both ways:

f1_2 <- function(x)
    sgd2$prop[1] * dnorm(x, sgd2$mean[1], sgd2$sd[1])
f2_2 <- function(x)
    sgd2$prop[2] * dnorm(x, sgd2$mean[2], sgd2$sd[2])
p <- p +
    stat_function(color = "red",
                  linetype = 2,
                  fun = f1_2) +
    stat_function(color = "blue",
                  linetype = 2,
                  fun = f2_2)
p

Minnesota Barley Yields

Another classic data set:

Total yield in bushels per acre for 10 varieties at 6 sites in Minnesota in each of two years, 1931 and 1932.

The raw data:

data(barley, package = "lattice")
head(barley)
##      yield   variety year            site
## 1 27.00000 Manchuria 1931 University Farm
## 2 48.86667 Manchuria 1931          Waseca
## 3 27.43334 Manchuria 1931          Morris
## 4 39.93333 Manchuria 1931       Crookston
## 5 32.96667 Manchuria 1931    Grand Rapids
## 6 28.96667 Manchuria 1931          Duluth

Some initial plots:

p1 <- ggplot(barley) + geom_point(aes(x = yield, y = variety))
p2 <- ggplot(barley) + geom_point(aes(x = yield, y = site))
cowplot::plot_grid(p1, p2)

Using color to separate yields in the two years:

p1 <- ggplot(barley) + geom_point(aes(x = yield, y = variety, color = year))
p2 <- ggplot(barley) + geom_point(aes(x = yield, y = site, color = year))
cowplot::plot_grid(p1, p2)

Can we also show site using symbol shape?

ggplot(barley) +
    geom_point(aes(x = yield,
                   y = variety,
                   color = year,
                   shape = site))

There is a lot of interference between shape and color.

Larger points may help:

ggplot(barley) +
    geom_point(aes(x = yield,
                   y = variety,
                   color = year,
                   shape = site),
               size = 2.5)

Jittering may also help:

ggplot(barley) +
    geom_point(aes(x = yield,
                   y = variety,
                   color = year,
                   shape = site),
               size = 2.5,
               position =
                   position_jitter(
                       height = 0.15,
                       width = 0))

Another approach: faceting to produce small multiples.

ggplot(barley) +
    geom_point(aes(x = yield,
                   y = variety,
                   color = year)) +
    facet_wrap(~site, ncol = 2)

Focusing on summaries can help.

A dot plot of average yields for each site and year:

barley_site_year <-
    group_by(barley, site, year) %>%
    summarize(yield = mean(yield)) %>%
    ungroup()

ggplot(barley_site_year) +
    geom_point(aes(y = site,
                   x = yield,
                   color = year),
               size = 3)

Adding lines can help comparing the changes. This is sometimes called a dumbbell chart:

barley_site_year <-
    group_by(barley, site, year) %>%
    summarize(yield = mean(yield)) %>%
    ungroup()

ggplot(barley_site_year) +
    geom_line(aes(y = site,
                  x = yield,
                  group = site),
              color = "darkgrey",
              size = 2) +
    geom_point(aes(y = site,
                   x = yield,
                   color = year),
               size = 4)

Another useful approach for showing repeated measurements is a slope graph:

library(ggrepel)
barley_site_year <-
    mutate(barley_site_year, year = fct_rev(year))
barley_site_year_1932 <-
    filter(barley_site_year, year == "1932")
ggplot(barley_site_year,
       aes(x = year, y = yield, group = site)) +
    geom_line() +
    geom_text_repel(aes(label = site),
                    data = barley_site_year_1932,
                    hjust = "left",
                    direction = "y") +
    scale_x_discrete(expand = expansion(mult = c(0.1, .25)),
                     position = "top") +
    labs(x = NULL, y = "Average Yield")

This emphasizes the reversal for Morris.

Bar charts are sometimes used for summaries, but dot plots are usually a better choice.

ggplot(barley_site_year) +
    geom_col(aes(x = yield,
                 y = site,
                 fill = year),
             size = 3,
             position = "dodge",
             width = .4)

Bar Charts and the Zero Base Line

Because of the way we perceive bars, it is important to use a zero base line for bar charts.

Hair and Eye Color Data

A data set recording the distribution of hair and eye color and sex in 592 statistics students.

HairEyeDF <- as.data.frame(HairEyeColor)
head(HairEyeDF)
##    Hair   Eye  Sex Freq
## 1 Black Brown Male   32
## 2 Brown Brown Male   53
## 3   Red Brown Male   10
## 4 Blond Brown Male    3
## 5 Black  Blue Male   11
## 6 Brown  Blue Male   50

The data set is available as a cross-tabulation.

as.data.frame converts it to a data frame.

Looking at the distribution of eye color:

eye <-
    group_by(HairEyeDF, Eye) %>%
    summarize(Freq = sum(Freq)) %>%
    ungroup()

ggplot(eye) +
    geom_col(aes(x = Eye,
                 y = Freq),
             position = "dodge")

Mapping eye color to bar color in addition to the horizontal axis position can help:

ggplot(eye) +
    geom_col(aes(x = Eye,
                 y = Freq,
                 fill = Eye),
             position = "dodge")

More sensible colors would be nice but require a bit of work:

hazel_rgb <-
    col2rgb("brown") * 0.75 + col2rgb("green") * 0.25
hazel <-
    do.call(rgb, as.list(hazel_rgb / 255))

cols <-
    c(Blue = colorspace::lighten(colorspace::desaturate("blue", 0.3), 0.3),
      Green = colorspace::lighten("forestgreen", 0.1),
      Brown = colorspace::lighten("brown", 0.0001), ## 0.3?
      Hazel = colorspace::lighten(hazel, 0.3))

pb <- ggplot(eye) +
    geom_col(aes(x = Eye,
                 y = Freq,
                 fill = Eye),
             position = "dodge") +
    scale_fill_manual(values = cols)
pb

A stacked bar chart can also be useful:

psb <- ggplot(eye) +
    geom_col(aes(x = "", y = Freq, fill = Eye), color = "lightgrey") +
    scale_fill_manual(values = cols)
psb

Changing to polar coordinates produces a pie chart:

(pp <- psb + coord_polar("y"))

The axis and grid are not helpful; a theme adjustment can remove them:

(pp <- pp + theme_void())

Themes provide a way to customize the non-data components of plots: i.e. titles, labels, fonts, background, grid lines, and legends.

Themes can be used to give plots a consistent customized look.

The ggthemes package provides a number of themes to emulate the style of different publications, for example theme_wsj and theme_economist.

How well do bar charts and pie charts work?

Some questions:

  • Which plot makes it easier to tell whether the proportion of brown-eyed students is larger or smaller than the proportion of blue-eyed students?

  • Which plot makes it easier to tell whether these proportions are larger or smaller than 1/2 or 1/4 or 1/3?

Looking at the proportions within hair color and sex:

eye_hairsex <-
    group_by(HairEyeDF, Hair, Sex) %>%
    mutate(Prop = Freq / sum(Freq)) %>%
    ungroup()

p1 <- ggplot(eye_hairsex) +
    geom_col(aes(x = Eye, y = Prop, fill = Eye)) +
    scale_fill_manual(values = cols) +
    facet_grid(Hair ~ Sex)
p2 <- ggplot(eye_hairsex) +
    geom_col(aes(x = "", y = Prop, fill = Eye)) +
    scale_fill_manual(values = cols) +
    coord_polar("y") +
    facet_grid(Hair ~ Sex) +
    theme_void()
cowplot::plot_grid(p1, p2)

A more complete ggplot template:

ggplot(data = <DATA>) +
    <GEOM>(mapping = aes(<MAPPINGS>),
           stat = <STAT>,
           position = <POSITION>) +
    < ... MORE GEOMS ... > +
    <COORDINATE_ADJUSTMENT> +
    <SCALE_ADJUSTMENT> +
    <FACETING> +
    <THEME_ADJUSTMENT>

Visual Perception and the Grammar of Graphics

Monthly River Flows

Monthly flow volumes recorded for a river in the pacific north-west.

An initial plot using default settings:

river <- scan(here::here("data/river.dat"))
rd <- data.frame(flow = river, month = seq_along(river))
(pp <- ggplot(rd) + geom_point(aes(x = month, y = flow)))

Changing the aspect ratio:

pp + coord_fixed(3.5)

Time series are often visualized with a line plot:

pl <- ggplot(rd) + geom_line(aes(x = month, y = flow))
pl + coord_fixed(3.5)

The seasonal variation can be seen with a line plot in the original aspect ratio:

pl

A Simple Model of Visual Perception

The eyes acquire an image, which is processed through three stages of memory:

  • Iconic memory

  • Working memory, or short-term memory

  • Long-term memory

The first processing stage of an image happens in iconic memory.

  • Images remain in iconic memory for less than a second.

  • Processing in iconic memory is massively parallel and automatic.

  • This is called preattentive processing.

Preattentive processing is a fast recognition process.

Meaningful visual chunks are moved from iconic memory to short term memory.

  • These chunks are used by conscious, or attentive, processing.

  • Attentive processing often involves conscious comparisons or search.

  • Short term memory is limited;

    • information is retained for only a few seconds;
    • only three or fours chunks can be held at a time.

Long term visual memory is built up over a lifetime, though infrequently used visual chunks may become lost.

Visual Design Implications

Try to make as much use of preattentive features as possible.

Recognize when preattentive features might mislead.

For features that require attentive processing, keep in mind that working memory is limited.

Some Terms for Describing Visualizations

Data to be visualized contains variables or attributes measured on individual items or cases.

Links are relationships that may exist among items, e.g. months within a year or countries within a continent.

Marks are individual geometric entities used to represent items: points. bars, etc.

Aesthetics or visual channels are the visual features of marks that can be used to encode attributes.

The aes(...) expressions establish the mapping between attributes and visual channels.

These ideas closely mirror the structure of the grammar of graphics as implemented in ggplot.

Munzner, T. (2014), Visualization Analysis and Design, CRC Press.

Wilkinson, L. (2005), The Grammar of Graphics, 2nd ed, Springer.

Channels and their Accuracy

A useful distinction among channels:

  • Magnitude channels can reflect order and numeric values, e.g. position on an axis, length, area, brightness.

  • Identity channels can distinguish different values but not reflect order, e.g. hue, shape, grouping.

Some channels are better at conveying information than others.

Munzner's ordering by accuracy:

Magnitude Channels (Ordered, Numerical) Identity Channels (Categorical)
Position on common scale Spatial grouping
Position on unaligned scale Color hue
Length (1D size) Shape
Tilt, angle
Area (2D size)
Depth (3D position)
Color luminance, saturation
Curvature, volume (3D size)

Line width is another channel; not sure there is agreement on its accuracy, but it is not high.

Visual Design Implications

Try to map the most important variables to the strongest channels.

Color

Color is very effective when used well.

But using color well is not easy.

Some of the issues:

  • Perception depends on context.

  • Simple color assignments may not separate equally well.

  • Effectiveness may vary with the medium (screen, projector, print).

  • Some people do not perceive the full specturm of colors.

  • Grey scale printing.

  • Some colors have cultural significance.

  • Cultural significance may vary among cultures and with time.

Color perception is relative:

Groups of colors that work well together are called palettes.

Some tools for selecting palettes include:

  • ColorBrewer; available in the RColorBrewer package.

  • HCL Wizard; also available as hclwizard in the colorspace package.

A note on rainbow colors.

A Grammar of Data Manipulation

The dplyr package provides a language, or grammar, for data manipulation.

The design of dplyr is strongly motivated by SQL.

The language contains a number of verbs that operate on tables.

The most commonly used verbs operate on a single data frame:

There are also a number of join verbs that merge several data frames into one.

Package tidyr provides more verbs, such as pivot_longer and pivot_wider for reshaping data frames.

The single table verbs can also be used with group_by to work separately on groups of rows.

More Examples

These examples start with raw data as you might receive it from a researcher, and involve reading and cleaning the data.

Common data formats you might encounter include

Tools are available for reading data in these formats into R.

Wind Turbines in Iowa

There are many wind turbines in Iowa.

Data is available from the U.S. Wind Turbine Database.

A snapshot is available is here as a CSV file.

  • CSV files are a common form of data exchange.

  • They are simple text files that are intended to be written and read by a computer.

  • Some CSV files include a header and a footer that need to he handled.

  • One issue is that a comma isn't a good separator in countries where it is the decimal separator!

  • A CSV file can be read using read.csv or readr::read_csv.

Reading the wind turbine data:

wind_turbines <- read.csv(here::here("data/us_wind.csv"), comment = "#")

Some data cleaning is needed.

Focus on the wind turbines in IOWA (19 is the state component of the FIPS county code for Iowa):

wt_IA <- filter(wind_turbines, t_fips %/% 1000 == 19)

Drop entries with missing longitude or latitude values:

wt_IA <- filter(wt_IA, ! is.na(xlong), ! is.na(ylat))

Some missing year values are encoded as -9999; replace these with NA:

wt_IA <- mutate(wt_IA, p_year = replace(p_year, p_year < 0, NA))

To show the locations of wind turbines on a map, load some map data:

iowa_sf <-
    sf::st_as_sf(maps::map("county", "iowa",
                           plot = FALSE,
                           fill = TRUE))

p <- ggplot() +
    geom_sf(data = iowa_sf) +
    ggthemes::theme_map()
p

Locations for all wind turbines in iowa:

p + geom_point(aes(xlong, ylat),
               data = wt_IA)

Using color to show when the wind turbines were built:

year_brk <- c(0, 2005, 2010, 2015, 2020)
year_lab <- c("before 2005",
              "2005-2009",
              "2010-2014",
              "2015-2020")
wt_IA <-
    mutate(wt_IA,
           year = cut(p_year,
                      breaks = year_brk,
                      labels = year_lab,
                      right = FALSE))
p + geom_point(aes(xlong,
                   ylat,
                   color = year),
               data = wt_IA,
               size = 3)

Cancer Map

The website http://www.cancer-rates.info/ia provides data on cancer incidence for a number of different cancers in Iowa.

The data for lung and bronchus cancer in 2011 are available in a csv file in the project.

We can read the file with read_csv from the readr package.

Looking at the file shows some things that need to be cleaned up:

  • Two header lines at the beginning.

  • Some footer lines.

  • Some values codes as ~.

The header can be handled by using skip = 2 in the read_csv call:

fname <- here::here("data/Invasive-Cancer-Incidence-Rates-by-County-in-Iowa-Lung-and-Bronchus-2011.csv")
d <- read_csv(fname, skip = 2)
head(d)
## # A tibble: 6 x 7
##   County  `Population at … Cases `Crude Rate` `Age-adjusted R… `95% Confidence …
##   <chr>              <dbl> <chr> <chr>        <chr>            <chr>            
## 1 Union              12570 20    159.11       115.82           69.86            
## 2 Ringgo…             5098 11    215.77       115.03           54.48            
## 3 Monroe              8044 12    149.18       109.99           56.24            
## 4 Page               15926 25    156.98       109.20           70.12            
## 5 Montgo…            10655 17    159.55       99.45            57.41            
## 6 Adams               3996 6     150.15       95.84            35.14            
## # … with 1 more variable: 95% Confidence Interval-Upper Limit <chr>

Let's focus on a few variables and give them more convenient names:

d <- select(d, county = 1, population = 2, count = 3)

The footer needs to be removed:

tail(d)
## # A tibble: 6 x 3
##   county                                                        population count
##   <chr>                                                              <dbl> <chr>
## 1 Butler                                                             14960 5    
## 2 Winneshiek                                                         21045 ~    
## 3 STATE                                                            3065223 2368 
## 4 Note: All rates are per 100,000. Rates are age-adjusted to t…         NA <NA> 
## 5 Rates generated on Jun 12, 2019.                                      NA <NA> 
## 6 Based on data released Nov 2017.                                      NA <NA>

One way to remove the footer:

d <- filter(d, ! is.na(population))
d <- filter(d, county != "STATE")

Changing count to numeric changes the ~ entries to missing values (NA) values:

d <- mutate(d, count = as.numeric(count))

In this case there are no zero case values; two ways to check:

count(d, count == 0)
## # A tibble: 2 x 2
##   `count == 0`     n
##   <lgl>        <int>
## 1 FALSE           95
## 2 NA               4
any(d$count == 0, na.rm = TRUE)
## [1] FALSE

It might be reasonable to assume these values where zero, so replace them with zeros:

d <- replace_na(d, list(count = 0))

A choropleth map uses color or shading to represent values measured for different geographic regions.

We will need to merge, or left join, the cancer data with the map date we loaded for the wind turbine map.

This requires a key on which to match the records in the cancer data and the map data.

For Iowa this can be done with the county name, but some care is needed:

d$county[1]
## [1] "Union"
iowa_sf$ID[1]
## [1] "iowa,adair"

Fixing case differences and dropping the iowa, prefix:

d <- mutate(d, cname = county, county = tolower(county))
iowa_sf <- mutate(iowa_sf, ID = sub("iowa,", "", ID))
iowa_sf <- rename(iowa_sf, county = ID)

Still not quite there:

setdiff(d$county, iowa_sf$county)
## [1] "o'brien"
setdiff(iowa_sf$county, d$county)
## [1] "obrien"

Drop the apostrophe in O'Brien:

d <- mutate(d, county = sub("'", "", county))

setdiff(d$county, iowa_sf$county)
## character(0)
setdiff(iowa_sf$county, d$county)
## character(0)

Define rate1K variable as the number of cases per 1000 inhabitants and left join the cancer data to the map data:

d <- mutate(d, rate1K = 1000 * (count / population))
md <- left_join(iowa_sf, d, "county")
head(md)
## Simple feature collection with 6 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -95.10526 ymin: 40.60552 xmax: -91.06018 ymax: 43.51041
## CRS:           EPSG:4326
##      county population count     cname    rate1K                           geom
## 1     adair       7565    10     Adair 1.3218771 MULTIPOLYGON (((-94.24583 4...
## 2     adams       3996     6     Adams 1.5015015 MULTIPOLYGON (((-94.70992 4...
## 3 allamakee      14204     7 Allamakee 0.4928189 MULTIPOLYGON (((-91.22634 4...
## 4 appanoose      12863    11 Appanoose 0.8551660 MULTIPOLYGON (((-92.63009 4...
## 5   audubon       6019     5   Audubon 0.8307028 MULTIPOLYGON (((-95.10526 4...
## 6    benton      26121    28    Benton 1.0719345 MULTIPOLYGON (((-92.06286 4...

A simple map:

ggplot(md) +
    geom_sf(aes(fill = rate1K))

An improved version:

library(ggthemes)
library(viridis)
ggplot(md) +
    geom_sf(aes(fill = rate1K),
            color = "grey") +
    scale_fill_viridis(name = "Rate per 1000") +
    theme_map()

A simple interactive version using plotly:

mdl <- mutate(md,
              label = paste(cname,
                            round(rate1K, 1),
                            population,
                            sep = "\n"))
p <- ggplot(mdl) +
    geom_sf(aes(fill = rate1K,
                text = label),
            color = "grey") +
    scale_fill_viridis(name = "Rate per 1000") +
    theme_map()

plotly::ggplotly(p, tooltip = "text")

The leaflet package supports more sophisticated interactive maps:

library(leaflet)
pal <- colorNumeric(palette = "viridis", domain = md$rate1K)
lab <- lapply(paste0(md$cname, "<BR>",
                     "Rate: ", round(md$rate1K, 1), "<BR>",
                     "Pop: ", scales::comma(md$population,
                                            accuracy = 1)),
              htmltools::HTML)
leaflet(sf::st_transform(md, 4326)) %>%
    addPolygons(weight = 2,
                color = "grey",
                fillColor = ~ pal(rate1K),
                fillOpacity = 1,
                highlightOptions = highlightOptions(color = "white",
                                                    weight = 2,
                                                    bringToFront = TRUE),
                label = lab) %>%
    addLegend(pal = pal, values = ~ rate1K, opacity = 1)

Unemployment Map

Local Area Unemployment Statistics page from the Bureau of Labor Statistics makes available county-level monthly unemployment data for a 14-month window.

The file for February 2020 through March 2021 is available is available at http://www.stat.uiowa.edu/~luke/data/laus/laucntycur14-2020.txt and in the project data folder.

This file is a text file but uses a non-standard separator.

It is designed for human readability and uses a comma as a thousands separator or grouping mark.

It also includes header and footer information.

It is still reasonably easy to read in.

One way to read the data into R is:

lausURL <- here::here("data/laucntycur14-2020.txt")
lausUS <- read.table(lausURL,
                     col.names = c("LAUSAreaCode", "State", "County",
                                   "Title", "Period",
                                   "LaborForce", "Employed",
                                   "Unemployed", "UnempRate"),
                     quote = '"', sep = "|", skip = 6,
                     stringsAsFactors = FALSE, strip.white = TRUE,
                     fill = TRUE)
footstart <- grep("------", lausUS$LAUSAreaCode)
lausUS <- lausUS[1 : (footstart - 1), ]

It may be useful to be able to access the county name and state name separately:

lausUS <- separate(lausUS, Title, c("cname", "scode"),
                   sep = ", ", fill = "right")

Check the variable types:

sapply(lausUS, class)
## LAUSAreaCode        State       County        cname        scode       Period 
##  "character"    "integer"    "integer"  "character"  "character"  "character" 
##   LaborForce     Employed   Unemployed    UnempRate 
##  "character"  "character"  "character"  "character"

The UnempRate variable is read as character data because of missing value encoding, so needs to be converted to numeric:

lausUS <- mutate(lausUS, UnempRate = as.numeric(UnempRate))

Check for missing values:

select_if(lausUS, anyNA) %>% names()
## [1] "scode"     "UnempRate"

The state code is missing for the District of Columbia:

select(lausUS, cname, scode) %>%
    filter(is.na(scode)) %>%
    unique()
##                  cname scode
## 1 District of Columbia  <NA>

March and April 2020 numbers were not available for Puerto Rico:

select(lausUS, scode, Period, UnempRate) %>%
    filter(is.na(UnempRate)) %>%
    unique()
##    scode Period UnempRate
## 1     PR Mar-20        NA
## 79    PR Apr-20        NA

To compute the national monthly unemployment rates over this period we need some more data cleaning:

lausUS <- mutate(lausUS,
                 Period = fct_inorder(Period),
                 LaborForce = as.numeric(gsub(",", "", LaborForce)),
                 Unemployed = as.numeric(gsub(",", "", Unemployed)))

Unemployment during this period was affected significantly by the COVID-19 pandemic.

A plot shows a large spike in April 2020:

group_by(lausUS, Period) %>%
    summarize(Unemployed = sum(Unemployed, na.rm = TRUE),
              LaborForce = sum(LaborForce, na.rm = TRUE),
              UnempRate = 100 * (Unemployed / LaborForce)) %>%
    ggplot(aes(Period, UnempRate, group = 1)) +
    geom_line()

A choropleth map can be used to look at how the impact was distributed across the country.

To show unemployment rates on a map we need to merge the unemployment data with map data.

To match county unemployment data and county shape data it is safer to use the numeric FIPS county code. This can be added with

lausUS <- mutate(lausUS, fips = State * 1000 + County)

Shape data for US counties can be obtained from a number of sources in a number of different formats.

Here is one approach:

counties_sf <- sf::st_as_sf(maps::map("county", plot = FALSE, fill = TRUE))
county.fips <-
    mutate(maps::county.fips, polyname = sub(":.*", "", polyname)) %>%
    unique()
counties_sf <- left_join(counties_sf, county.fips, c("ID" = "polyname"))
states_sf <- sf::st_as_sf(maps::map("state", plot = FALSE, fill = TRUE))

Some summaries over the period can be computed as

summaryUS <- group_by(lausUS, County, State, fips) %>%
    summarize(avg_unemp = mean(UnempRate, na.rm = TRUE),
              max_unemp = max(UnempRate, na.rm = TRUE),
              apr_unemp = UnempRate[Period == "Apr-20"]) %>%
    ungroup()
## `summarise()` has grouped output by 'County', 'State'. You can override using the `.groups` argument.
head(summaryUS)
## # A tibble: 6 x 6
##   County State  fips avg_unemp max_unemp apr_unemp
##    <int> <int> <dbl>     <dbl>     <dbl>     <dbl>
## 1      1     1  1001      4.65      10.9      10.9
## 2      1     4  4001     12.7       17.6      16.6
## 3      1     5  5001      4.06       5.2       4.8
## 4      1     6  6001      8.8       14.6      14.6
## 5      1     8  8001      8.3       12.7      12.7
## 6      1     9  9001      8.31      11.7       8.2

A choropleth map of the April 2020 unemployment rates:

left_join(counties_sf, summaryUS, "fips") %>%
    ggplot() +
    geom_sf(aes(fill = apr_unemp)) +
    scale_fill_viridis(name = "Rate", na.value = "red") +
    theme_map() +
    geom_sf(data = states_sf, col = "grey", fill = NA)

Using a very visible color for missing data is useful, at least during exploration.

anti_join can show the county geometry that does not have an entry in the unemployment data:

anti_join(counties_sf, summaryUS, "fips")
## Simple feature collection with 1 feature and 2 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -103.0121 ymin: 42.99475 xmax: -102.0782 ymax: 43.68803
## CRS:           EPSG:4326
##                     ID  fips                           geom
## 1 south dakota,shannon 46113 MULTIPOLYGON (((-102.8115 4...

Shannon County, SD (FIPS 46113), was renamed to Oglala Lakota County in June 2015 and given a new FIPS code, 46102.

The geometry data table needs to be updated:

counties_sf <- mutate(counties_sf, fips = replace(fips, fips == 46113, 46102))

With the updated data the map is now complete:

left_join(counties_sf, summaryUS, "fips") %>%
    ggplot() +
    geom_sf(aes(fill = apr_unemp)) +
    scale_fill_viridis(name = "Rate", na.value = "red") +
    theme_map() +
    geom_sf(data = states_sf, col = "grey", fill = NA)

Gapminder Childhood Mortality Data

The gapminder package provides a subset of the data from the Gapminder web site.

Additional data sets are available.

  • A data set on childhood mortality is available locally as a csv file or an Excel file.

  • The Excel file is also available in the project data folder.

  • The numbers represent number of deaths within the first five years per 1000 births.

Many researchers like to manage their data in a spreadsheet.

  • Being able to read such a sheet directly greatly helps keeping the workflow reproducible.

  • Many spreadsheets contain header, footers, and other annotations to aid a human viewer.

  • As long as the data are in a rectangular region it is usually not hard to extract them programmatically.

Loading the data:

library(readxl)
gcm <- read_excel(here::here("data/gapminder-under5mortality.xlsx"))

A first look:

head(gcm, 3)
## # A tibble: 3 x 217
##   `Under five mortality` `1800.0` `1801.0` `1802.0` `1803.0` `1804.0` `1805.0`
##   <chr>                     <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
## 1 Abkhazia                    NA       NA       NA       NA       NA       NA 
## 2 Afghanistan                469.     469.     469.     469.     469.     469.
## 3 Akrotiri and Dhekelia       NA       NA       NA       NA       NA       NA 
## # … with 210 more variables: 1806.0 <dbl>, 1807.0 <dbl>, 1808.0 <dbl>,
## #   1809.0 <dbl>, 1810.0 <dbl>, 1811.0 <dbl>, 1812.0 <dbl>, 1813.0 <dbl>,
## #   1814.0 <dbl>, 1815.0 <dbl>, 1816.0 <dbl>, 1817.0 <dbl>, 1818.0 <dbl>,
## #   1819.0 <dbl>, 1820.0 <dbl>, 1821.0 <dbl>, 1822.0 <dbl>, 1823.0 <dbl>,
## #   1824.0 <dbl>, 1825.0 <dbl>, 1826.0 <dbl>, 1827.0 <dbl>, 1828.0 <dbl>,
## #   1829.0 <dbl>, 1830.0 <dbl>, 1831.0 <dbl>, 1832.0 <dbl>, 1833.0 <dbl>,
## #   1834.0 <dbl>, 1835.0 <dbl>, 1836.0 <dbl>, 1837.0 <dbl>, 1838.0 <dbl>,
## #   1839.0 <dbl>, 1840.0 <dbl>, 1841.0 <dbl>, 1842.0 <dbl>, 1843.0 <dbl>,
## #   1844.0 <dbl>, 1845.0 <dbl>, 1846.0 <dbl>, 1847.0 <dbl>, 1848.0 <dbl>,
## #   1849.0 <dbl>, 1850.0 <dbl>, 1851.0 <dbl>, 1852.0 <dbl>, 1853.0 <dbl>,
## #   1854.0 <dbl>, 1855.0 <dbl>, 1856.0 <dbl>, 1857.0 <dbl>, 1858.0 <dbl>,
## #   1859.0 <dbl>, 1860.0 <dbl>, 1861.0 <dbl>, 1862.0 <dbl>, 1863.0 <dbl>,
## #   1864.0 <dbl>, 1865.0 <dbl>, 1866.0 <dbl>, 1867.0 <dbl>, 1868.0 <dbl>,
## #   1869.0 <dbl>, 1870.0 <dbl>, 1871.0 <dbl>, 1872.0 <dbl>, 1873.0 <dbl>,
## #   1874.0 <dbl>, 1875.0 <dbl>, 1876.0 <dbl>, 1877.0 <dbl>, 1878.0 <dbl>,
## #   1879.0 <dbl>, 1880.0 <dbl>, 1881.0 <dbl>, 1882.0 <dbl>, 1883.0 <dbl>,
## #   1884.0 <dbl>, 1885.0 <dbl>, 1886.0 <dbl>, 1887.0 <dbl>, 1888.0 <dbl>,
## #   1889.0 <dbl>, 1890.0 <dbl>, 1891.0 <dbl>, 1892.0 <dbl>, 1893.0 <dbl>,
## #   1894.0 <dbl>, 1895.0 <dbl>, 1896.0 <dbl>, 1897.0 <dbl>, 1898.0 <dbl>,
## #   1899.0 <dbl>, 1900.0 <dbl>, 1901.0 <dbl>, 1902.0 <dbl>, 1903.0 <dbl>,
## #   1904.0 <dbl>, 1905.0 <dbl>, …

This data set is in wide format, with one column per year.

A long version with a year and a value column is useful for working with ggplot.

A better first variable name:

names(gcm)[1] <- "country"

Convert to long format:

tgcm <-
    pivot_longer(gcm, -1, names_to = "year", values_to = "u5mort") %>%
    mutate(year = as.numeric(year))
head(tgcm, 3)
## # A tibble: 3 x 3
##   country   year u5mort
##   <chr>    <dbl>  <dbl>
## 1 Abkhazia  1800     NA
## 2 Abkhazia  1801     NA
## 3 Abkhazia  1802     NA

Some explorations:

p <- ggplot(tgcm) +
    geom_line(aes(year,
                  u5mort,
                  group = country),
              alpha = 0.3)
plotly::ggplotly(p)

Some selected countries:

countries <- c("United States",
               "United Kingdom",
               "Germany",
               "China",
               "Egypt")
filter(tgcm, country %in% countries) %>%
    ggplot() +
    geom_line(aes(x = year,
                  y = u5mort,
                  color = country))

Examining the missing values:

tgcm_miss <-
    group_by(tgcm, country) %>%
    summarize(anyNA = anyNA(u5mort)) %>%
    filter(anyNA) %>%
    pull(country)

p <- filter(tgcm,
            country %in% tgcm_miss) %>%
    ggplot(aes(x = year,
               y = u5mort,
               group = country)) +
    geom_line(na.rm = TRUE) +
    xlim(c(1940, 2020))
plotly::ggplotly(p)
LS0tCnRpdGxlOiAiQmFzaWMgRGF0YSBXcmFuZ2xpbmcgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSIgphdXRob3I6ICJMdWtlIFRpZXJuZXkiCmluc3RpdHV0ZTogIlVuaXZlcnNpdHkgb2YgSW93YSIKZGF0ZTogIjIxIEp1bmUsIDIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSkKeGFyaW5nYW5FeHRyYTo6dXNlX2NsaXBib2FyZCgpCmxpYnJhcnkoZ2dwbG90MikKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSArCiAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsKICAgICAgICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmV5MzAiLCBmaWxsID0gTkEpKSkKaGVyZV9yZWwgPC0gZnVuY3Rpb24ocGF0aCkKICAgIGlmIChmaWxlLmV4aXN0cyhwYXRoKSkgcGF0aCBlbHNlIGZpbGUucGF0aCgiLi4iLCBwYXRoKQpgYGAKCiMjIEludHJvZHVjdGlvbgoKIyMjIE91dGxpbmUKCkluIHRoaXMgY2xhc3MgSSB3aWxsCgoqIEJyaWVmbHkgb3V0bGluZSB0aGUgaGlzdG9yeSBvZiBSLgoKKiBVc2luZyBzb21lIGV4YW1wbGVzIGJyaWVmbHkgc2hvdyBob3cgdG8gZG8gZGF0YSB3cmFuZ2xpbmcKICBhbmQgdmlzdWFsaXplIGRhdGEgaW4gUi4KCk1hdGVyaWFscyBmb3IgdGhpcyBjbGFzcyBhcmUgYXZhaWxhYmxlIG9uIEdpdEh1YiBhdAo8aHR0cHM6Ly9naXRodWIuY29tL2x0aWVybmV5L1NJQlMtV1YtMjAyMS5naXQ+LgoKKiBZb3UgY2FuIGFjY2VzcyBpdCBhcyBhbiBSU3R1ZGlvIHByb2plY3QgYnkgZm9sbG93aW5nIHRoZSBtZW51IHNlbGVjdGlvbgogICoqRmlsZSA+IE5ldyBQcm9qZWN0ID4gVmVyc2lvbiBDb250cm9sID4gR2l0KiogYW5kIHNwZWNpZnlpbmcgdGhpcyBVUkwuCgoqIFlvdSBjYW4gdXNlIHRoZSBgZ2l0YCBjb21tYW5kIGxpbmUgY2xpZW50IHdpdGgKICAgIGBgYHNoZWxsCmdpdCBjbG9uZSBodHRwczovL2dpdGh1Yi5jb20vbHRpZXJuZXkvU0lCUy1XVi0yMDIxLmdpdAogICAgYGBgCgpNYXRlcmlhbHMgZm9yIG91ciBfRGF0YSBWaXN1YWxpemF0aW9uIGFuZCBEYXRhClRlY2hub2xvZ2llc18gY291cnNlIGFyZSBhdmFpbGFibGUgYXQKCjxodHRwOi8vd3d3LnN0YXQudWlvd2EuZWR1L35sdWtlL2NsYXNzZXMvU1RBVDQ1ODAtMjAyMS8+CgoKIyMjIFRvb2xzCgpTb21lIHRvb2xzIEkgd2lsbCBiZSB1c2luZzoKCiogVGhlIFtSU3R1ZGlvXShodHRwczovL3d3dy5yc3R1ZGlvbi5jb20pIElERS4KCiogTWFueSBmZWF0dXJlcyBmcm9tIHRoZSBiYXNpYyBbUl0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZykgZGlzdHJpYnV0aW9uLgoKKiBTb21lIHRvb2xzIGZyb20gdGhlIFtfdGlkeXZlcnNlX10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pLgoKKiBUaGUgW2BnZ3Bsb3RgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgYmFzZWQgb24KICB0aGUgX0dyYW1tYXIgb2YgR3JhcGhpY3NfIGZyYW1ld29yay4KCk1vc3Qgb2YgdGhlIHBhY2thZ2VzIGFyZSBsb2FkZWQgYnkgbG9hZGluZyB0aGUgYHRpZHl2ZXJzZWAgcGFja2FnZToKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBSZWZlcmVuY2VzCgpVc2VmdWwgcmVmZXJlbmNlczoKCj4gSGFkbGV5IFdpY2toYW0gYW5kIEdhcnJldHQgR3JvbGVtdW5kICgyMDE2KSwgW19SIGZvciBEYXRhCj4gU2NpZW5jZV9dKGh0dHA6Ly9yNGRzLmhhZC5jby5uei8pLCBPJ1JlaWxseS4KCj4gQ2xhdXMgTy4gV2lsa2UgKDIwMTkpLCBbX0Z1bmRhbWVudGFscyBvZiBEYXRhCj4gIFZpc3VhbGl6YXRpb25fXShodHRwczovL3NlcmlhbG1lbnRvci5jb20vZGF0YXZpei8pLCBPJ1JlaWxseS4KCj4gS2llcmFuIEhlYWx5ICgyMDE4KSBbX0RhdGEgVmlzdWFsaXphdGlvbjogQSBwcmFjdGljYWwKPiBpbnRyb2R1Y3Rpb25fXShodHRwOi8vc29jdml6LmNvLyksIFByaW5jZXRvbgoKPiBSYWZhZWwgQS4gSXJpemFycnkgKDIwMTkpLCBbSW50cm9kdWN0aW9uIHRvIERhdGEgU2NpZW5jZTogX0RhdGEKPiBBbmFseXNpcyBhbmQgUHJlZGljdGlvbiBBbGdvcml0aG1zIHdpdGgKPiBSX10oaHR0cHM6Ly9yYWZhbGFiLmdpdGh1Yi5pby9kc2Jvb2svKSwgQ2hhcG1hbiAmIEhhbGwvQ1JDLiAoW0Jvb2sKPiBzb3VyY2Ugb24gR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vcmFmYWxhYi9kc2Jvb2spKQoKKipBc2sgcXVlc3Rpb25zIGFueSB0aW1lISoqCgogCiMjIFRoZSBSIExhbmd1YWdlCgojIyMgQmFja2dyb3VuZAoKUiBpcyBhIGxhbmd1YWdlIGZvciBkYXRhIGFuYWx5c2lzIGFuZCBncmFwaGljcy4KCiogUiB3YXMgb3JpZ2luYWxseSBkZXZlbG9wZWQgYnkgUm9iZXJ0IEdlbnRsZW1hbiBhbmQgUm9zcyBJaGFrYSBpbiB0aGUKICBlYXJseSAxOTkwJ3MgZm9yIGEgTWFjaW50b3NoIGNvbXB1dGVyIGxhYiBhdCBVLiBvZiBBdWNrbGFuZCwgTmV3IFplYWxhbmQuCgoqIFIgaXMgYmFzZWQgb24gdGhlIFMgbGFuZ3VhZ2UgZGV2ZWxvcGVkIGJ5IEpvaG4gQ2hhbWJlcnMgYW5kCiAgb3RoZXJzIGF0IEJlbGwgTGFicy4KClIgaXMgYW4gT3BlbiBTb3VyY2UgcHJvamVjdC4KCiogU2luY2UgMTk5NyBSIGlzIGRldmVsb3BlZCBhbmQgbWFpbnRhaW5lZCBieSB0aGUgUi1jb3JlIGdyb3VwLAogIHdpdGggYXJvdW5kIDIwIG1lbWJlcnMgbG9jYXRlZCBpbiBtYW9yIHRoYW4gMTAgZGlmZmVyZW50IGNvdW50cmllcy4KCiogUiBpcyB3aWRlbHkgdXNlZCBpbiB0aGUgZmllbGQgb2Ygc3RhdGlzdGljcyBhbmQgYmV5b25kLCBlc3BlY2lhbGx5IGluCiAgdW5pdmVyc2l0eSBlbnZpcm9ubWVudHMuCgoqIFIgaGFzIGJlY29tZSB0aGUgcHJpbWFyeSBmcmFtZXdvcmsgZm9yIGRldmVsb3BpbmcgYW5kIG1ha2luZyBhdmFpbGFibGUKICBuZXcgc3RhdGlzdGljYWwgbWV0aG9kb2xvZ3kuCgoqIE1hbnkgKG5vdyBvdmVyIDE3LDAwMCkgZXh0ZW5zaW9uIHBhY2thZ2VzIGFyZSBhdmFpbGFibGUgdGhyb3VnaCBDUkFOIG9yCiAgc2ltaWxhciByZXBvc2l0b3JpZXMuCgojIyMgV29ya2luZyB3aXRoIFIKClIgaXMgZGVzaWduZWQgZm9yIGludGVyYWN0aXZlIGRhdGEgZXhwbG9yYXRpb24uCgoqIEludGVyYWN0aW9uIGlzIHRocm91Z2ggYSBfcmVhZC1ldmFsLXByaW50IGxvb3AgKFJFUEwpXy4KCiogVGhpcyBpcyBhbHNvIGNhbGxlZCBhIF9jb21tYW5kIGxpbmUgaW50ZXJmYWNlIChDTEkpXy4KCkFsbCBjb21wdXRhdGlvbnMgYXJlIHNwZWNpZmllZCBpbiB0aGUgUiBsYW5ndWFnZS4KCiogRXZlbiBmb3Igc2ltcGxlIHRhc2tzIHlvdSBuZWVkIHRvIGtub3cgYSBsaXR0bGUgb2YgdGhlIGxhbmd1YWdlLgoKKiBBZnRlciBsZWFybmluZyB0byBkbyBzaW1wbGUgdGFza3MgeW91IGtub3cgc29tZSBvZiB0aGUgbGFuZ3VhZ2UuCgpUaGUgbGFuZ3VhZ2UgaXMgdXNlZCB0bwoKKiBwcmVwYXJlIGRhdGEgZm9yIGFuYWx5c2lzOwoKKiBzcGVjaWZ5IGluZGl2aWR1YWwgYW5hbHlzZXM7CgoqIHByb2dyYW0gcmVwZWF0ZWQgb3Igc2ltaWxhciBhbmFseXNlczsKCiogcHJvZ3JhbSBuZXcgbWV0aG9kcyBvZiBhbmFseXNpcy4KClNwZWNpZnlpbmcgdGhlc2UgdGFza3MgaW4gYSBsYW5ndWFnZSBzdXBwb3J0cyBfcmVwcm9kdWNpYmxlIHJlc2VhcmNoXy4KClRoZSBSIGxhbmd1YWdlIG9wZXJhdGVzIG9uIHZlY3RvcnMgYW5kIGFycmF5cy4KCkNvbW1vbmx5IHVzZWQgZGF0YSB0eXBlcyBhcmU6CgoqIGludGVnZXIgYW5kIG51bWVyaWMgdmVjdG9yczsKCiogbG9naWNhbCB2ZWN0b3JzOwoKKiBjaGFyYWN0ZXIgdmVjdG9yczsKCiogZmFjdG9ycy4KCkFsbCBiYXNpYyB2ZWN0b3IgdHlwZXMgc3VwcG9ydCBtaXNzaW5nIChgTkFgKSB2YWx1ZXMuCgpBcml0aG1ldGljIG9wZXJhdGlvbnMgYXJlIHZlY3Rvcml6ZWQgdG8gb3BlcmF0ZSBlbGVtZW50LXdpc2Ugb24gdmVjdG9ycy4KCkRhdGEgdmVjdG9ycyBhcmUgdXN1YWxseSBjb21iaW5lZCBpbnRvIHRhYmxlLWxpa2Ugb2JqZWN0cyBjYWxsZWQgX2RhdGEKZnJhbWVzXy4KCgojIyMgVGhlIERhdGEgQW5hbHlzaXMgUHJvY2VzcwoKQSBmaWd1cmUgdGhhdCBzaG93cyB0aGUgc3RlcHMgdXN1YWxseSBpbnZvbHZlZCBpbiBhIGRhdGEgYW5hbHlzaXMKcHJvamVjdDoKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkobm9tbm9tbCkKYGBgCjxjZW50ZXI+CmBgYHtub21ub21sLCBlY2hvID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA4fQojcGFkZGluZzogMjUKI2ZvbnRzaXplOiAxOAojZmlsbDogI0UxREFGRjsgI0Q0QTlGRgojc3Ryb2tlOiAjODUxNUM3CiNsaW5ld2lkdGg6IDIKCltJbXBvcnRdIC0+IFtVbmRlcnN0YW5kXQpbVW5kZXJzdGFuZCB8CiAgW1dyYW5nbGVdIC0+IFtWaXN1YWxpemVdCiAgW1Zpc3VhbGl6ZV0gLT4gW01vZGVsXQogIFtNb2RlbF0gLT4gW1dyYW5nbGVdCl0KW1VuZGVyc3RhbmRdIC0+IFtDb21tdW5pY2F0ZV0KYGBgCjwvY2VudGVyPgoKVGhlc2Ugc3RlcHMgYXJlIG9mdGVuIHJlcGVhdGVkIG1hbnkgdGltZXMsIHNvIGl0IGlzIGltcG9ydGFudCB0byBtYWtlCnlvdXIgd29yayByZXByb2R1Y2libGUuCgoKIyMjIFJlcHJvZHVjaWJsZSBEYXRhIEFuYWx5c2lzCgpNYWtpbmcgeW91ciB3b3JrIHJlcHJvZHVjaWJsZToKCiogU2F2ZSB5b3Ugd29yayBpbiBhIHRleHQgZmlsZSBvciBub3RlYm9vay4KCiogVHJhY2sgY2hhbmdlcyB0byB5b3VyIGZpbGVzIHdpdGggYSB2ZXJzaW9uIGNvbnRyb2wgc3lzdGVtIGxpa2UKICBbYGdpdGBdKGh0dHBzOi8vZ2l0LXNjbS5jb20vKS4KCiogVXNlIGEgc3lzdGVtIGxpa2UgW1JtYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIHRvCiAgcHJlcGFyZSB5b3VyIHJlcG9ydHMuCgpUaGlzIGFsbG93cyB5b3UgdG8gcmUtY3JlYXRlIHlvdXIgcmVwb3J0IHdoZW4gZGF0YSBjaGFuZ2VzIChhcyBpdApvZnRlbiB3aWxsISkuCgpBIGdvb2QgcmVzb3VyY2UgZm9yIHNldHRpbmcgdXAgeW91ciB0b29scyB0byBzdXBwb3J0IHRoaXMgaXMgW19IYXBweQpHaXQgYW5kIEdpdEh1YiBmb3IgdGhlIHVzZVJfXShodHRwczovL2hhcHB5Z2l0d2l0aHIuY29tLykuCgoKIyMgU29tZSBFeGFtcGxlcwoKIyMjIEV4YW1wbGUgRGF0YSBTZXRzCgpXaGVuIHdvcmtpbmcgd2l0aCByZXNlYXJjaCBkYXRhLCBhIGZpcnN0IHN0ZXAgaXMgdXN1YWxseSB0byByZWFkIGFuZApjbGVhbiB0aGUgZGF0YS4KCldlJ2xsIHB1dCB0aGF0IG9mZiBmb3IgYSBsaXR0bGUgd2hpbGUgYW5kIHdvcmsgd2l0aCBzb21lIGRhdGEgc2V0cwptYWRlIGF2YWlsYWJsZSBpbiBSIHBhY2thZ2VzLgoKRGF0YSBzZXRzIGF2YWlsYWJsZSBpbiBSIHBhY2thZ2VzIGluY2x1ZGU6CgoqIG1hbnkgY2xhc3NpYyBkYXRhIHNldHM7CgoqIG5ld2VyLCBvZnRlbiBsYXJnZXIsIGRhdGEgc2V0cyB1c2VmdWwgZm9yIGxlYXJuaW5nOwoKKiBjdXJyZW50IGRhdGEgb2J0YWluZWQgYnkgcXVlcnlpbmcgd2ViIEFQSXMuCgoKIyMjIE9sZCBGYWl0aGZ1bCBFcnVwdGlvbnMKCkEgc2ltcGxlIGNsYXNzaWMgZGF0YSBzZXQgaXMgdGhlIGBnZXlzZXJgIGRhdGEgZnJhbWUgYXZhaWxhYmxlIGluCnBhY2thZ2UgYE1BU1NgLgoKYGBge3J9CmRhdGEoZ2V5c2VyLCBwYWNrYWdlID0gIk1BU1MiKQpkaW0oZ2V5c2VyKQpoZWFkKGdleXNlciwgNCkKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KYGhlYWRgIGFuZCBgdGFpbGAgcmV0dXJuIHRoZSBmaXJzdCBhbmQgbGFzdCBmZXcgcm93cyBvZiBhIGRhdGEgZnJhbWUuCgpUaGV5IGFyZSB1c2VmdWwgZm9yIHF1aWNrIHNhbml0eSBjaGVja3MuCjwvZGl2PgoKVGhlIHJvd3MgcmVwcmVzZW50IG1lYXN1cmVtZW50cyByZWNvcmRlZCBmb3IgZXJ1cHRpb25zIG9mIHRoZSBbX09sZApGYWl0aGZ1bF9dKGh0dHBzOi8vd3d3LnllbGxvd3N0b25lcGFyay5jb20vdGhpbmdzLXRvLWRvL2dleXNlcnMtaG90LXNwcmluZ3MvYWJvdXQtb2xkLWZhaXRoZnVsLykKZ2V5c2VyIGluIFllbGxvd3N0b25lIE5hdGlvbmFsIFBhcmssIFd5b21pbmcuCgpUaGUgdmFyaWFibGVzIGFyZToKCiogYHdhaXRpbmdgOiB0aGUgdGltZSBpbiBtaW51dGVzIHNpbmNlIHRoZSBwcmV2aW91cyBlcnVwdGlvbjsKCiogYGR1cmF0aW9uYDogdGhlIGR1cmF0aW9uIGluIG1pbnV0ZXMgb2YgdGhlIGVydXB0aW9uLgoKVGhlIGR1cmF0aW9ucyBoYXZlIGEgYmltb2RhbCBkaXN0cmlidXRpb246CgpgYGB7ciBnZXlzZXItaGlzdCwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoZ2V5c2VyKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGR1cmF0aW9uKSwKICAgICAgICAgICAgICAgICAgIGJpbnMgPSAxNSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIpCmBgYApgYGB7ciBnZXlzZXItaGlzdCwgZXZhbCA9IEZBTFNFfQpgYGAKClRoaXMgaXMgW2BnZ3Bsb3RgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pIGNvZGUgZm9yIGNyZWF0aW5nIGEKaGlzdG9ncmFtLgoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CkEgYmFzaWMgdGVtcGxhdGUgZm9yIGNyZWF0aW5nIGEgcGxvdCB3aXRoIGBnZ3Bsb3RgOgoKPCEtLSAjIG5vbGludCBzdGFydCAtLT4KCmBgYHIKZ2dwbG90KGRhdGEgPSA8REFUQT4pICsKICAgIDxHRU9NPihtYXBwaW5nID0gYWVzKDxNQVBQSU5HUz4pKQpgYGAKCjwhLS0gIyBub2xpbnQgZW5kIC0tPgo8L2Rpdj4KCkFuIGludGVyZXN0aW5nIHF1ZXN0aW9uIGlzIHdoZXRoZXIgdGhlIGR1cmF0aW9uIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QKd2hlbiB0aGUgX25leHRfIGVydXB0aW9uIHdpbGwgb2NjdXIuCgpBIHBsb3Qgb2YgdGhlIF9wcmV2aW91c18gZHVyYXRpb24gYWdhaW5zdCB0aGUgd2FpdGluZyB0aW1lIHRvIHRoZQpjdXJyZW50IGVydXB0aW9uOgoKYGBge3IgZ2V5c2VyLXNjYXR0ZXIsIGVjaG8gPSBGQUxTRX0KZ2dwbG90KGdleXNlcikgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IGxhZyhkdXJhdGlvbiksCiAgICAgICAgICAgICAgICAgICB5ID0gd2FpdGluZykpCmBgYAoKYGBge3IgZ2V5c2VyLXNjYXR0ZXIsIGV2YWwgPSBGQUxTRX0KYGBgCgpJdCBsb29rcyBsaWtlIGEgdXNlZnVsIHJ1bGUgd291bGQgYmUgdG8gZXhwZWN0IGEgc2hvcnRlciB3YWl0aW5nIHRpbWUKYWZ0ZXIgYSBzaG9ydGVyIGVydXB0aW9uIGR1cmF0aW9uLgoKQW4gaW50ZXJlc3RpbmcgZmVhdHVyZToKCk1hbnkgZHVyYXRpb25zIGFyZSByZWNvcmRlZCBhcyAyIG9yIDQgbWludXRlcy4KClRoaXMgY2FuIGFsc28gYmUgc2VlbiBpbiBhIGhpc3RvZ3JhbSB3aXRoIGEgc21hbGwgYmluIHdpZHRoOgoKYGBge3IgZ2V5c2VyLWhpc3QtbmFycm93LCBlY2hvID0gRkFMU0V9CnAgPC0gZ2dwbG90KGdleXNlcikgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBkdXJhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gc3RhdChkZW5zaXR5KSksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjEpCnAKYGBgCmBgYHtyIGdleXNlci1oaXN0LW5hcnJvdywgZXZhbCA9IEZBTFNFfQpgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPiAKYGdncGxvdGAgcHJvZHVjZXMgYSBwbG90IG9iamVjdC4KCkRyYXdpbmcgb25seSBoYXBwZW5zIHdoZW4gdGhlIG9iamVjdCBpcyBwcmludGVkLgo8L2Rpdj4KCkRvZXMgdGhpcyByb3VuZGluZyBtYXR0ZXI/CgoqIEZvciBtYW55IGFuYWx5c2VzIGl0IHByb2JhYmx5IGRvZXNuJ3QuCgoqIEl0IG1pZ2h0IGlmIHlvdSB3YW50ZWQgdG8gZml0IG5vcm1hbCBkaXN0cmlidXRpb25zIHRvIHRoZSB0d28gZ3JvdXBzLgoKVGFraW5nIDMgbWludXRlcyBhcyB0aGUgZGl2aWRlIGJldHdlZW4gc2hvcnQgYW5kIGxvbmcgZHVyYXRpb25zIHdlIGNhbgpmaXJzdCBwaWNrIG91dCB0aGUgc2hvcnQgYW5kIGxvbmcgZHVyYXRpb25zOgoKYGBge3J9CmQgPC0gZ2V5c2VyJGR1cmF0aW9uCmRfc2hvcnQgPC0gZFtkIDwgM10KZF9sb25nIDwtIGRbZCA+PSAzXQpgYGAKClRoZW4gY29tcHV0ZSB0aGUgbWVhbnMgYW5kIHN0YW5kYXJkIGRldmlhdGlvbnMgYXMKCmBgYHtyfQptZWFuKGRfc2hvcnQpCnNkKGRfc2hvcnQpCm1lYW4oZF9sb25nKQpzZChkX2xvbmcpCm1lYW4oZCA+PSAzKQpgYGAKCkFuIGFwcHJvYWNoIHRoYXQgc2NhbGVzIGJldHRlcjoKCkNvbXB1dGUgZ3JvdXAgc3VtbWFyaWVzIHVzaW5nIHRvb2xzIGZyb20gdGhlIGBkcGx5cmAgdGlkeXZlcnNlCnBhY2thZ2UuCgpGaXJzdCwgYWRkIGEgYHR5cGVgIHZhcmlhYmxlOgoKYGBge3J9CmdleXNlciA8LSBtdXRhdGUoZ2V5c2VyLCB0eXBlID0gaWZlbHNlKGR1cmF0aW9uIDwgMywgInNob3J0IiwgImxvbmciKSkKYGBgCgpUaGUgc3VtbWFyaWVzIGNhbiB0aGVuIGJlIGNvbXB1dGVkIGFzCgpgYGB7cn0Kc2dkIDwtIHN1bW1hcml6ZShncm91cF9ieShnZXlzZXIsIHR5cGUpLAogICAgICAgICAgICAgICAgIG1lYW4gPSBtZWFuKGR1cmF0aW9uKSwKICAgICAgICAgICAgICAgICBzZCA9IHNkKGR1cmF0aW9uKSwKICAgICAgICAgICAgICAgICBuID0gbigpKQooc2dkIDwtIG11dGF0ZShzZ2QsIHByb3AgPSBuIC8gc3VtKG4pKSkKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KYHN1bW1hcml6ZWAsIGBncm91cF9ieWAsIGFuZCBgbXV0YXRlYCBhcmUgZnJvbSB0aGUgYGRwbHlyYCBwYWNrYWdlCnRoYXQgaW1wbGVtZW50cyBhIF9ncmFtbWFyIG9mIGRhdGEgbWFuaXB1bGF0aW9uXy4KPC9kaXY+CgpUaGlzIGNvbXB1dGF0aW9uIGNhbiBhbHNvIGJlIHdyaXR0ZW4gdXNpbmcgdGhlIF9mb3J3YXJkIHBpcGUgb3BlcmF0b3JfIGAlPiVgOgoKYGBge3J9CnNnZCA8LQogICAgZ3JvdXBfYnkoZ2V5c2VyLCB0eXBlKSAlPiUKICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbihkdXJhdGlvbiksCiAgICAgICAgICAgICAgc2QgPSBzZChkdXJhdGlvbiksCiAgICAgICAgICAgICAgbiA9IG4oKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUocHJvcCA9IG4gLyBzdW0obikpCnNnZApgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgpUaGUgcGlwZSBvcGVyYXRvciBhbGxvd3MgYSBzZXF1ZW5jZSBvZiBvcGVyYXRpb25zIHRvIGJlIGNoYWluZWQgdG9nZXRoZXIuCgpUaGUgbGVmdC1oYW5kIG9wZXJhdGlvbiBpcyBwYXNzZWQgaW1wbGljaXRseSBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8KdGhlIGZ1bmN0aW9uIGNhbGxlZCBvbiB0aGUgcmlnaHQuCjwvZGl2PgoKT25lIHdheSB0byBzaG93IHRoZSBzdXBlcmltcG9zZWQgbm9ybWFsIGRlbnNpdGllczoKCmBgYHtyIGdleXNlci1oaXN0LWRlbnMsIGVjaG8gPSBGQUxTRX0KZjEgPC0gZnVuY3Rpb24oeCkKICAgIHNnZCRwcm9wWzFdICogZG5vcm0oeCwgc2dkJG1lYW5bMV0sIHNnZCRzZFsxXSkKZjIgPC0gZnVuY3Rpb24oeCkKICAgIHNnZCRwcm9wWzJdICogZG5vcm0oeCwgc2dkJG1lYW5bMl0sIHNnZCRzZFsyXSkKcCA8LSBwICsKICAgIHN0YXRfZnVuY3Rpb24oY29sb3IgPSAicmVkIiwgZnVuID0gZjEpICsKICAgIHN0YXRfZnVuY3Rpb24oY29sb3IgPSAiYmx1ZSIsIGZ1biA9IGYyKQpwCmBgYApgYGB7ciBnZXlzZXItaGlzdC1kZW5zLCBldmFsID0gRkFMU0V9CmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+IApBIGBnZ3Bsb3RgIGNhbiBjb25zaXN0IG9mIHNldmVyYWwgX2xheWVyc18uCjwvZGl2PgoKVGhlIG1lYW5zIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zIGFyZSBhZmZlY3RlZCBieSB0aGUgcm91bmRpbmcuCgpTdW1tYXJpZXMgdGhhdCBvbWl0IHZhbHVlcyBlcXVhbCB0byAyIG9yIDQgbWludXRlcyBjYW4gYmUgY29tcHV0ZWQgYXMKCmBgYHtyfQpnZXlzZXIyIDwtIGZpbHRlcihnZXlzZXIsIGR1cmF0aW9uICE9IDIsIGR1cmF0aW9uICE9IDQpCnNnZDIgPC0KICAgIGdyb3VwX2J5KGdleXNlcjIsIHR5cGUpICU+JQogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKGR1cmF0aW9uKSwKICAgICAgICAgICAgICBzZCA9IHNkKGR1cmF0aW9uKSwKICAgICAgICAgICAgICBuID0gbigpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkKc2dkMgpgYGAKCkEgcGxvdCBzaG93aW5nIGN1cnZlcyBjb21wdXRlZCBib3RoIHdheXM6CgpgYGB7ciBnZXlzZXItaGlzdC1kZW5zLTIsIGVjaG8gPSBGQUxTRX0KZjFfMiA8LSBmdW5jdGlvbih4KQogICAgc2dkMiRwcm9wWzFdICogZG5vcm0oeCwgc2dkMiRtZWFuWzFdLCBzZ2QyJHNkWzFdKQpmMl8yIDwtIGZ1bmN0aW9uKHgpCiAgICBzZ2QyJHByb3BbMl0gKiBkbm9ybSh4LCBzZ2QyJG1lYW5bMl0sIHNnZDIkc2RbMl0pCnAgPC0gcCArCiAgICBzdGF0X2Z1bmN0aW9uKGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgICAgZnVuID0gZjFfMikgKwogICAgc3RhdF9mdW5jdGlvbihjb2xvciA9ICJibHVlIiwKICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgICBmdW4gPSBmMl8yKQpwCmBgYApgYGB7ciBnZXlzZXItaGlzdC1kZW5zLTIsIGV2YWwgPSBGQUxTRX0KYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFLCBlY2hvID0gRkFMU0V9CiMjIEZhbmNpZXIgdmVyc2lvbiB0aGF0IGdldHMgYSBjb2xvciBsZWdlbmQuCiMjIENvdWxkIGFsc28gZ2V0IGEgbGluZSB0eXBlIGxlZ2VuZC4KcCA8LSBnZ3Bsb3QoZ2V5c2VyKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGR1cmF0aW9uLCB5ID0gc3RhdChkZW5zaXR5KSksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gImdyZXkiLCBjb2xvciA9ICJibGFjayIsIGJpbnMgPSA1MCkKcCA8LSBwICsgCiAgICBzdGF0X2Z1bmN0aW9uKGFlcyhjb2xvciA9IHR5cGUpLAogICAgICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKHNnZCwgdHlwZSA9PSAibG9uZyIpLAogICAgICAgICAgICAgICAgICBmdW4gPSBmdW5jdGlvbih4KQogICAgICAgICAgICAgICAgICAgICAgICAgIHNnZCRwcm9wWzFdICogZG5vcm0oeCwgc2dkJG1lYW5bMV0sIHNnZCRzZFsxXSkpICsKICAgIHN0YXRfZnVuY3Rpb24oYWVzKGNvbG9yID0gdHlwZSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoc2dkLCB0eXBlID09ICJzaG9ydCIpLAogICAgICAgICAgICAgICAgICBmdW4gPSBmdW5jdGlvbih4KQogICAgICAgICAgICAgICAgICAgICAgICAgIHNnZCRwcm9wWzJdICogZG5vcm0oeCwgc2dkJG1lYW5bMl0sIHNnZCRzZFsyXSkpCnAKCnAgPC0gcCArCiAgICAgc3RhdF9mdW5jdGlvbihhZXMoY29sb3IgPSB0eXBlKSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihzZ2QyLCB0eXBlID09ICJsb25nIiksCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgICAgZnVuID0gZnVuY3Rpb24oeCkKICAgICAgICAgICAgICAgICAgICAgICAgICBzZ2QyJHByb3BbMV0gKiBkbm9ybSh4LCBzZ2QyJG1lYW5bMV0sIHNnZDIkc2RbMV0pKSArCiAgICBzdGF0X2Z1bmN0aW9uKGFlcyhjb2xvciA9IHR5cGUpLAogICAgICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKHNnZDIsIHR5cGUgPT0gInNob3J0IiksCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgICAgZnVuID0gZnVuY3Rpb24oeCkKICAgICAgICAgICAgICAgICAgICAgICAgICBzZ2QyJHByb3BbMl0gKiBkbm9ybSh4LCBzZ2QyJG1lYW5bMl0sIHNnZDIkc2RbMl0pKQpwCmBgYAoKCiMjIyBNaW5uZXNvdGEgQmFybGV5IFlpZWxkcwoKQW5vdGhlciBjbGFzc2ljIGRhdGEgc2V0OgoKVG90YWwgeWllbGQgaW4gYnVzaGVscyBwZXIgYWNyZSBmb3IgMTAgdmFyaWV0aWVzIGF0IDYgc2l0ZXMgaW4KTWlubmVzb3RhIGluIGVhY2ggb2YgdHdvIHllYXJzLCAxOTMxIGFuZCAxOTMyLgoKVGhlIHJhdyBkYXRhOgoKYGBge3J9CmRhdGEoYmFybGV5LCBwYWNrYWdlID0gImxhdHRpY2UiKQpoZWFkKGJhcmxleSkKYGBgCgpTb21lIGluaXRpYWwgcGxvdHM6CgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnAxIDwtIGdncGxvdChiYXJsZXkpICsgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLCB5ID0gdmFyaWV0eSkpCnAyIDwtIGdncGxvdChiYXJsZXkpICsgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLCB5ID0gc2l0ZSkpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIpCmBgYAoKVXNpbmcgY29sb3IgdG8gc2VwYXJhdGUgeWllbGRzIGluIHRoZSB0d28geWVhcnM6CgpgYGB7ciwgZmlnLndpZHRoID0gMTJ9CnAxIDwtIGdncGxvdChiYXJsZXkpICsgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLCB5ID0gdmFyaWV0eSwgY29sb3IgPSB5ZWFyKSkKcDIgPC0gZ2dwbG90KGJhcmxleSkgKyBnZW9tX3BvaW50KGFlcyh4ID0geWllbGQsIHkgPSBzaXRlLCBjb2xvciA9IHllYXIpKQpjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyKQoKYGBgCgpDYW4gd2UgYWxzbyBzaG93IGBzaXRlYCB1c2luZyBzeW1ib2wgc2hhcGU/CgpgYGB7ciBiYXJsZXktY29sb3Itc3ltLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDd9CmdncGxvdChiYXJsZXkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSB5aWVsZCwKICAgICAgICAgICAgICAgICAgIHkgPSB2YXJpZXR5LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyLAogICAgICAgICAgICAgICAgICAgc2hhcGUgPSBzaXRlKSkKYGBgCmBgYHtyIGJhcmxleS1jb2xvci1zeW0sIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGVyZSBpcyBhIGxvdCBvZiBfaW50ZXJmZXJlbmNlXyBiZXR3ZWVuIHNoYXBlIGFuZCBjb2xvci4KCkxhcmdlciBwb2ludHMgbWF5IGhlbHA6CgpgYGB7ciBiYXJsZXktY29sb3Itc3ltLTIsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gN30KZ2dwbG90KGJhcmxleSkgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLAogICAgICAgICAgICAgICAgICAgeSA9IHZhcmlldHksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHllYXIsCiAgICAgICAgICAgICAgICAgICBzaGFwZSA9IHNpdGUpLAogICAgICAgICAgICAgICBzaXplID0gMi41KQpgYGAKYGBge3IgYmFybGV5LWNvbG9yLXN5bS0yLCBldmFsID0gRkFMU0V9CmBgYAoKX0ppdHRlcmluZ18gbWF5IGFsc28gaGVscDoKCgpgYGB7ciBiYXJsZXktY29sb3Itc3ltLTMsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gN30KZ2dwbG90KGJhcmxleSkgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLAogICAgICAgICAgICAgICAgICAgeSA9IHZhcmlldHksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHllYXIsCiAgICAgICAgICAgICAgICAgICBzaGFwZSA9IHNpdGUpLAogICAgICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9CiAgICAgICAgICAgICAgICAgICBwb3NpdGlvbl9qaXR0ZXIoCiAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gMC4xNSwKICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDApKQpgYGAKYGBge3IgYmFybGV5LWNvbG9yLXN5bS0zLCBldmFsID0gRkFMU0V9CmBgYAoKQW5vdGhlciBhcHByb2FjaDogX2ZhY2V0aW5nXyB0byBwcm9kdWNlIF9zbWFsbCBtdWx0aXBsZXNfLgoKYGBge3IgYmFybGV5LWZhY2V0LCBldmFsID0gRkFMU0V9CmdncGxvdChiYXJsZXkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSB5aWVsZCwKICAgICAgICAgICAgICAgICAgIHkgPSB2YXJpZXR5LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyKSkgKwogICAgZmFjZXRfd3JhcCh+c2l0ZSwgbmNvbCA9IDIpCmBgYApgYGB7ciBiYXJsZXktZmFjZXQsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDd9CmBgYAoKRm9jdXNpbmcgb24gc3VtbWFyaWVzIGNhbiBoZWxwLgoKQSBfZG90IHBsb3RfIG9mIGF2ZXJhZ2UgeWllbGRzIGZvciBlYWNoIHNpdGUgYW5kIHllYXI6CgpgYGB7ciBiYXJsZXktYXZnLWRvdCwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDd9CmJhcmxleV9zaXRlX3llYXIgPC0KICAgIGdyb3VwX2J5KGJhcmxleSwgc2l0ZSwgeWVhcikgJT4lCiAgICBzdW1tYXJpemUoeWllbGQgPSBtZWFuKHlpZWxkKSkgJT4lCiAgICB1bmdyb3VwKCkKCmdncGxvdChiYXJsZXlfc2l0ZV95ZWFyKSArCiAgICBnZW9tX3BvaW50KGFlcyh5ID0gc2l0ZSwKICAgICAgICAgICAgICAgICAgIHggPSB5aWVsZCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0geWVhciksCiAgICAgICAgICAgICAgIHNpemUgPSAzKQpgYGAKYGBge3IgYmFybGV5LWF2Zy1kb3QsIGV2YWwgPSBGQUxTRX0KYGBgCgpBZGRpbmcgbGluZXMgY2FuIGhlbHAgY29tcGFyaW5nIHRoZSBjaGFuZ2VzLiBUaGlzIGlzIHNvbWV0aW1lcyBjYWxsZWQKYSBfZHVtYmJlbGwgY2hhcnRfOgoKCmBgYHtyIGJhcmxleS1hdmctZG90LTIsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA3fQpiYXJsZXlfc2l0ZV95ZWFyIDwtCiAgICBncm91cF9ieShiYXJsZXksIHNpdGUsIHllYXIpICU+JQogICAgc3VtbWFyaXplKHlpZWxkID0gbWVhbih5aWVsZCkpICU+JQogICAgdW5ncm91cCgpCgpnZ3Bsb3QoYmFybGV5X3NpdGVfeWVhcikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gc2l0ZSwKICAgICAgICAgICAgICAgICAgeCA9IHlpZWxkLAogICAgICAgICAgICAgICAgICBncm91cCA9IHNpdGUpLAogICAgICAgICAgICAgIGNvbG9yID0gImRhcmtncmV5IiwKICAgICAgICAgICAgICBzaXplID0gMikgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IHNpdGUsCiAgICAgICAgICAgICAgICAgICB4ID0geWllbGQsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHllYXIpLAogICAgICAgICAgICAgICBzaXplID0gNCkKYGBgCmBgYHtyIGJhcmxleS1hdmctZG90LTIsIGV2YWwgPSBGQUxTRX0KYGBgCgpBbm90aGVyIHVzZWZ1bCBhcHByb2FjaCBmb3Igc2hvd2luZyByZXBlYXRlZCBtZWFzdXJlbWVudHMgaXMgYSBfc2xvcGUKZ3JhcGhfOgoKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KbGlicmFyeShnZ3JlcGVsKQpiYXJsZXlfc2l0ZV95ZWFyIDwtCiAgICBtdXRhdGUoYmFybGV5X3NpdGVfeWVhciwgeWVhciA9IGZjdF9yZXYoeWVhcikpCmJhcmxleV9zaXRlX3llYXJfMTkzMiA8LQogICAgZmlsdGVyKGJhcmxleV9zaXRlX3llYXIsIHllYXIgPT0gIjE5MzIiKQpnZ3Bsb3QoYmFybGV5X3NpdGVfeWVhciwKICAgICAgIGFlcyh4ID0geWVhciwgeSA9IHlpZWxkLCBncm91cCA9IHNpdGUpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gc2l0ZSksCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhcmxleV9zaXRlX3llYXJfMTkzMiwKICAgICAgICAgICAgICAgICAgICBoanVzdCA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAieSIpICsKICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMSwgLjI1KSksCiAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gInRvcCIpICsKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSAiQXZlcmFnZSBZaWVsZCIpCmBgYAoKVGhpcyBlbXBoYXNpemVzIHRoZSByZXZlcnNhbCBmb3IgTW9ycmlzLgoKX0JhciBjaGFydHNfIGFyZSBzb21ldGltZXMgdXNlZCBmb3Igc3VtbWFyaWVzLCBidXQgZG90IHBsb3RzIGFyZQp1c3VhbGx5IGEgYmV0dGVyIGNob2ljZS4KCmBgYHtyIGJhcmxleS1hdmctYmFyLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gN30KZ2dwbG90KGJhcmxleV9zaXRlX3llYXIpICsKICAgIGdlb21fY29sKGFlcyh4ID0geWllbGQsCiAgICAgICAgICAgICAgICAgeSA9IHNpdGUsCiAgICAgICAgICAgICAgICAgZmlsbCA9IHllYXIpLAogICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgICBwb3NpdGlvbiA9ICJkb2RnZSIsCiAgICAgICAgICAgICB3aWR0aCA9IC40KQpgYGAKYGBge3IgYmFybGV5LWF2Zy1iYXIsIGV2YWwgPSBGQUxTRX0KYGBgCgojIyMgQmFyIENoYXJ0cyBhbmQgdGhlIFplcm8gQmFzZSBMaW5lCgpCZWNhdXNlIG9mIHRoZSB3YXkgd2UgcGVyY2VpdmUgYmFycywgaXQgaXMgaW1wb3J0YW50IHRvIHVzZSBhIFt6ZXJvCmJhc2UgbGluZSBmb3IgYmFyCmNoYXJ0c10oaHR0cHM6Ly9mbG93aW5nZGF0YS5jb20vMjAxNS8wOC8zMS9iYXItY2hhcnQtYmFzZWxpbmVzLXN0YXJ0LWF0LXplcm8vKS4KCiFbXShgciBoZXJlX3JlbCgiaW1nL3ZpejMtNTIweDI5NC5qcGciKWApCiFbXShgciBoZXJlX3JlbCgiaW1nL3ZpejUtNTIweDI4MC5qcGciKWApCgoKIyMjIEhhaXIgYW5kIEV5ZSBDb2xvciBEYXRhCgpBIGRhdGEgc2V0IHJlY29yZGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGhhaXIgYW5kIGV5ZSBjb2xvciBhbmQgc2V4IGluCjU5MiBzdGF0aXN0aWNzIHN0dWRlbnRzLgoKYGBge3J9CkhhaXJFeWVERiA8LSBhcy5kYXRhLmZyYW1lKEhhaXJFeWVDb2xvcikKaGVhZChIYWlyRXllREYpCmBgYAoKVGhlIGRhdGEgc2V0IGlzIGF2YWlsYWJsZSBhcyBhIF9jcm9zcy10YWJ1bGF0aW9uXy4KCmBhcy5kYXRhLmZyYW1lYCBjb252ZXJ0cyBpdCB0byBhIGRhdGEgZnJhbWUuCgpMb29raW5nIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZXllIGNvbG9yOgoKYGBge3IgZXllLWJhciwgZWNobyA9IEZBTFNFfQpleWUgPC0KICAgIGdyb3VwX2J5KEhhaXJFeWVERiwgRXllKSAlPiUKICAgIHN1bW1hcml6ZShGcmVxID0gc3VtKEZyZXEpKSAlPiUKICAgIHVuZ3JvdXAoKQoKZ2dwbG90KGV5ZSkgKwogICAgZ2VvbV9jb2woYWVzKHggPSBFeWUsCiAgICAgICAgICAgICAgICAgeSA9IEZyZXEpLAogICAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKYGBge3IgZXllLWJhciwgZXZhbCA9IEZBTFNFfQpgYGAKCk1hcHBpbmcgZXllIGNvbG9yIHRvIGJhciBjb2xvciBpbiBhZGRpdGlvbiB0byB0aGUgaG9yaXpvbnRhbCBheGlzCnBvc2l0aW9uIGNhbiBoZWxwOgoKYGBge3IgZXllLWJhci0yLCBlY2hvID0gRkFMU0V9CmdncGxvdChleWUpICsKICAgIGdlb21fY29sKGFlcyh4ID0gRXllLAogICAgICAgICAgICAgICAgIHkgPSBGcmVxLAogICAgICAgICAgICAgICAgIGZpbGwgPSBFeWUpLAogICAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKYGBge3IgZXllLWJhci0yLCBldmFsID0gRkFMU0V9CmBgYAoKTW9yZSBzZW5zaWJsZSBjb2xvcnMgd291bGQgYmUgbmljZSBidXQgcmVxdWlyZSBhIGJpdCBvZiB3b3JrOgoKYGBge3IgZXllLWJhci0zLCBlY2hvID0gRkFMU0V9CmhhemVsX3JnYiA8LQogICAgY29sMnJnYigiYnJvd24iKSAqIDAuNzUgKyBjb2wycmdiKCJncmVlbiIpICogMC4yNQpoYXplbCA8LQogICAgZG8uY2FsbChyZ2IsIGFzLmxpc3QoaGF6ZWxfcmdiIC8gMjU1KSkKCmNvbHMgPC0KICAgIGMoQmx1ZSA9IGNvbG9yc3BhY2U6OmxpZ2h0ZW4oY29sb3JzcGFjZTo6ZGVzYXR1cmF0ZSgiYmx1ZSIsIDAuMyksIDAuMyksCiAgICAgIEdyZWVuID0gY29sb3JzcGFjZTo6bGlnaHRlbigiZm9yZXN0Z3JlZW4iLCAwLjEpLAogICAgICBCcm93biA9IGNvbG9yc3BhY2U6OmxpZ2h0ZW4oImJyb3duIiwgMC4wMDAxKSwgIyMgMC4zPwogICAgICBIYXplbCA9IGNvbG9yc3BhY2U6OmxpZ2h0ZW4oaGF6ZWwsIDAuMykpCgpwYiA8LSBnZ3Bsb3QoZXllKSArCiAgICBnZW9tX2NvbChhZXMoeCA9IEV5ZSwKICAgICAgICAgICAgICAgICB5ID0gRnJlcSwKICAgICAgICAgICAgICAgICBmaWxsID0gRXllKSwKICAgICAgICAgICAgIHBvc2l0aW9uID0gImRvZGdlIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykKcGIKYGBgCmBgYHtyIGV5ZS1iYXItMywgZXZhbCA9IEZBTFNFfQpgYGAKCkEgX3N0YWNrZWQgYmFyIGNoYXJ0XyBjYW4gYWxzbyBiZSB1c2VmdWw6CgpgYGB7ciBleWUtYmFyLXN0YWNrZWQsIGVjaG8gPSBGQUxTRX0KcHNiIDwtIGdncGxvdChleWUpICsKICAgIGdlb21fY29sKGFlcyh4ID0gIiIsIHkgPSBGcmVxLCBmaWxsID0gRXllKSwgY29sb3IgPSAibGlnaHRncmV5IikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykKcHNiCmBgYApgYGB7ciBleWUtYmFyLXN0YWNrZWQsIGV2YWwgPSBGQUxTRX0KYGBgCgpDaGFuZ2luZyB0byBwb2xhciBjb29yZGluYXRlcyBwcm9kdWNlcyBhIF9waWUgY2hhcnRfOgoKYGBge3IgZXllLXBpZSwgZWNobyA9IEZBTFNFfQoocHAgPC0gcHNiICsgY29vcmRfcG9sYXIoInkiKSkKYGBgCmBgYHtyIGV5ZS1waWUsIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGUgYXhpcyBhbmQgZ3JpZCBhcmUgbm90IGhlbHBmdWw7IGEgX3RoZW1lXyBhZGp1c3RtZW50IGNhbiByZW1vdmUgdGhlbToKCmBgYHtyIGV5ZS1waWUtMiwgZWNobyA9IEZBTFNFfQoocHAgPC0gcHAgKyB0aGVtZV92b2lkKCkpCmBgYApgYGB7ciBleWUtcGllLTIsIGV2YWwgPSBGQUxTRX0KYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KVGhlbWVzIHByb3ZpZGUgYSB3YXkgdG8gY3VzdG9taXplIHRoZSBub24tZGF0YSBjb21wb25lbnRzIG9mIHBsb3RzOgppLmUuIHRpdGxlcywgbGFiZWxzLCBmb250cywgYmFja2dyb3VuZCwgZ3JpZCBsaW5lcywgYW5kIGxlZ2VuZHMuCgpUaGVtZXMgY2FuIGJlIHVzZWQgdG8gZ2l2ZSBwbG90cyBhIGNvbnNpc3RlbnQgY3VzdG9taXplZCBsb29rLgoKVGhlIGBnZ3RoZW1lc2AgcGFja2FnZSBwcm92aWRlcyBhIG51bWJlciBvZiB0aGVtZXMgdG8gZW11bGF0ZSB0aGUKc3R5bGUgb2YgZGlmZmVyZW50IHB1YmxpY2F0aW9ucywgZm9yIGV4YW1wbGUgYHRoZW1lX3dzamAgYW5kCmB0aGVtZV9lY29ub21pc3RgLgo8L2Rpdj4KCkhvdyB3ZWxsIGRvIGJhciBjaGFydHMgYW5kIHBpZSBjaGFydHMgd29yaz8KCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwfQpjb3dwbG90OjpwbG90X2dyaWQocGIsIHBwKQpgYGAKClNvbWUgcXVlc3Rpb25zOgoKKiBXaGljaCBwbG90IG1ha2VzIGl0IGVhc2llciB0byB0ZWxsIHdoZXRoZXIgdGhlIHByb3BvcnRpb24gb2YKICBicm93bi1leWVkIHN0dWRlbnRzIGlzIGxhcmdlciBvciBzbWFsbGVyIHRoYW4gdGhlIHByb3BvcnRpb24gb2YKICBibHVlLWV5ZWQgc3R1ZGVudHM/CgoqIFdoaWNoIHBsb3QgbWFrZXMgaXQgZWFzaWVyIHRvIHRlbGwgd2hldGhlciB0aGVzZSBwcm9wb3J0aW9ucyBhcmUKICBsYXJnZXIgb3Igc21hbGxlciB0aGFuIDEvMiBvciAxLzQgb3IgMS8zPwoKTG9va2luZyBhdCB0aGUgcHJvcG9ydGlvbnMgd2l0aGluIGhhaXIgY29sb3IgYW5kIHNleDoKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmV5ZV9oYWlyc2V4IDwtCiAgICBncm91cF9ieShIYWlyRXllREYsIEhhaXIsIFNleCkgJT4lCiAgICBtdXRhdGUoUHJvcCA9IEZyZXEgLyBzdW0oRnJlcSkpICU+JQogICAgdW5ncm91cCgpCgpwMSA8LSBnZ3Bsb3QoZXllX2hhaXJzZXgpICsKICAgIGdlb21fY29sKGFlcyh4ID0gRXllLCB5ID0gUHJvcCwgZmlsbCA9IEV5ZSkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICAgIGZhY2V0X2dyaWQoSGFpciB+IFNleCkKcDIgPC0gZ2dwbG90KGV5ZV9oYWlyc2V4KSArCiAgICBnZW9tX2NvbChhZXMoeCA9ICIiLCB5ID0gUHJvcCwgZmlsbCA9IEV5ZSkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICAgIGNvb3JkX3BvbGFyKCJ5IikgKwogICAgZmFjZXRfZ3JpZChIYWlyIH4gU2V4KSArCiAgICB0aGVtZV92b2lkKCkKY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMikKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KQSBtb3JlIGNvbXBsZXRlIGBnZ3Bsb3RgIHRlbXBsYXRlOgoKPCEtLSAjIG5vbGludCBzdGFydCAtLT4KCmBgYHIKZ2dwbG90KGRhdGEgPSA8REFUQT4pICsKICAgIDxHRU9NPihtYXBwaW5nID0gYWVzKDxNQVBQSU5HUz4pLAogICAgICAgICAgIHN0YXQgPSA8U1RBVD4sCiAgICAgICAgICAgcG9zaXRpb24gPSA8UE9TSVRJT04+KSArCiAgICA8IC4uLiBNT1JFIEdFT01TIC4uLiA+ICsKICAgIDxDT09SRElOQVRFX0FESlVTVE1FTlQ+ICsKICAgIDxTQ0FMRV9BREpVU1RNRU5UPiArCiAgICA8RkFDRVRJTkc+ICsKICAgIDxUSEVNRV9BREpVU1RNRU5UPgpgYGAKPCEtLSAjIG5vbGludCBlbmQgLS0+Cgo8L2Rpdj4KCgojIyBWaXN1YWwgUGVyY2VwdGlvbiBhbmQgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MKCiMjIyBNb250aGx5IFJpdmVyIEZsb3dzCgpNb250aGx5IGZsb3cgdm9sdW1lcyByZWNvcmRlZCBmb3IgYSByaXZlciBpbiB0aGUgcGFjaWZpYyBub3J0aC13ZXN0LgoKQW4gaW5pdGlhbCBwbG90IHVzaW5nIGRlZmF1bHQgc2V0dGluZ3M6CgpgYGB7ciwgZmlnLndpZHRoID0gOCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnJpdmVyIDwtIHNjYW4oaGVyZTo6aGVyZSgiZGF0YS9yaXZlci5kYXQiKSkKcmQgPC0gZGF0YS5mcmFtZShmbG93ID0gcml2ZXIsIG1vbnRoID0gc2VxX2Fsb25nKHJpdmVyKSkKKHBwIDwtIGdncGxvdChyZCkgKyBnZW9tX3BvaW50KGFlcyh4ID0gbW9udGgsIHkgPSBmbG93KSkpCmBgYAoKQ2hhbmdpbmcgdGhlIF9hc3BlY3QgcmF0aW9fOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcHAgKyBjb29yZF9maXhlZCgzLjUpCmBgYAoKVGltZSBzZXJpZXMgYXJlIG9mdGVuIHZpc3VhbGl6ZWQgd2l0aCBhIGxpbmUgcGxvdDoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnBsIDwtIGdncGxvdChyZCkgKyBnZW9tX2xpbmUoYWVzKHggPSBtb250aCwgeSA9IGZsb3cpKQpwbCArIGNvb3JkX2ZpeGVkKDMuNSkKYGBgCgpUaGUgc2Vhc29uYWwgdmFyaWF0aW9uIGNhbiBiZSBzZWVuIHdpdGggYSBsaW5lIHBsb3QgaW4gdGhlIG9yaWdpbmFsCmFzcGVjdCByYXRpbzoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnBsCmBgYAoKIyMjIEEgU2ltcGxlIE1vZGVsIG9mIFZpc3VhbCBQZXJjZXB0aW9uCgpUaGUgZXllcyBhY3F1aXJlIGFuIGltYWdlLCB3aGljaCBpcyBwcm9jZXNzZWQgdGhyb3VnaCB0aHJlZSBzdGFnZXMgb2YKbWVtb3J5OgoKKiBJY29uaWMgbWVtb3J5CgoqIFdvcmtpbmcgbWVtb3J5LCBvciBzaG9ydC10ZXJtIG1lbW9yeQoKKiBMb25nLXRlcm0gbWVtb3J5CgpUaGUgZmlyc3QgcHJvY2Vzc2luZyBzdGFnZSBvZiBhbiBpbWFnZSBoYXBwZW5zIGluIGljb25pYyBtZW1vcnkuCgoqIEltYWdlcyByZW1haW4gaW4gaWNvbmljIG1lbW9yeSBmb3IgbGVzcyB0aGFuIGEgc2Vjb25kLgoKKiBQcm9jZXNzaW5nIGluIGljb25pYyBtZW1vcnkgaXMgbWFzc2l2ZWx5IHBhcmFsbGVsIGFuZCBhdXRvbWF0aWMuCgoqIFRoaXMgaXMgY2FsbGVkIF9wcmVhdHRlbnRpdmUgcHJvY2Vzc2luZ18uCgpQcmVhdHRlbnRpdmUgcHJvY2Vzc2luZyBpcyBhIGZhc3QgcmVjb2duaXRpb24gcHJvY2Vzcy4KCk1lYW5pbmdmdWwgdmlzdWFsIGNodW5rcyBhcmUgbW92ZWQgZnJvbSBpY29uaWMgbWVtb3J5IHRvIHNob3J0IHRlcm0gbWVtb3J5LgoKKiBUaGVzZSBjaHVua3MgYXJlIHVzZWQgYnkgY29uc2Npb3VzLCBvciBhdHRlbnRpdmUsIHByb2Nlc3NpbmcuCgoqIEF0dGVudGl2ZSBwcm9jZXNzaW5nIG9mdGVuIGludm9sdmVzIGNvbnNjaW91cyBjb21wYXJpc29ucyBvciBzZWFyY2guCgoqIFNob3J0IHRlcm0gbWVtb3J5IGlzIGxpbWl0ZWQ7CgogICAgKiBpbmZvcm1hdGlvbiBpcyByZXRhaW5lZCBmb3Igb25seSBhIGZldyBzZWNvbmRzOwogICAgKiBvbmx5IHRocmVlIG9yIGZvdXJzIGNodW5rcyBjYW4gYmUgaGVsZCBhdCBhIHRpbWUuCgpMb25nIHRlcm0gdmlzdWFsIG1lbW9yeSBpcyBidWlsdCB1cCBvdmVyIGEgbGlmZXRpbWUsIHRob3VnaAppbmZyZXF1ZW50bHkgdXNlZCB2aXN1YWwgY2h1bmtzIG1heSBiZWNvbWUgbG9zdC4KCiMjIyMgVmlzdWFsIERlc2lnbiBJbXBsaWNhdGlvbnMKClRyeSB0byBtYWtlIGFzIG11Y2ggdXNlIG9mIHByZWF0dGVudGl2ZSBmZWF0dXJlcyBhcyBwb3NzaWJsZS4KClJlY29nbml6ZSB3aGVuIHByZWF0dGVudGl2ZSBmZWF0dXJlcyBtaWdodCBtaXNsZWFkLgoKRm9yIGZlYXR1cmVzIHRoYXQgcmVxdWlyZSBhdHRlbnRpdmUgcHJvY2Vzc2luZywga2VlcCBpbiBtaW5kIHRoYXQKd29ya2luZyBtZW1vcnkgaXMgbGltaXRlZC4KCgojIyMgU29tZSBUZXJtcyBmb3IgRGVzY3JpYmluZyBWaXN1YWxpemF0aW9ucwoKRGF0YSB0byBiZSB2aXN1YWxpemVkIGNvbnRhaW5zIF92YXJpYWJsZXNfIG9yIF9hdHRyaWJ1dGVzXyBtZWFzdXJlZCBvbgppbmRpdmlkdWFsIF9pdGVtc18gb3IgX2Nhc2VzXy4KCl9MaW5rc18gYXJlIHJlbGF0aW9uc2hpcHMgdGhhdCBtYXkgZXhpc3QgYW1vbmcgaXRlbXMsIGUuZy4gbW9udGhzCndpdGhpbiBhIHllYXIgb3IgY291bnRyaWVzIHdpdGhpbiBhIGNvbnRpbmVudC4KCl9NYXJrc18gYXJlIGluZGl2aWR1YWwgZ2VvbWV0cmljIGVudGl0aWVzIHVzZWQgdG8gcmVwcmVzZW50IGl0ZW1zOgpwb2ludHMuIGJhcnMsIGV0Yy4KCl9BZXN0aGV0aWNzXyBvciBfdmlzdWFsIGNoYW5uZWxzXyBhcmUgdGhlIHZpc3VhbCBmZWF0dXJlcyBvZiBtYXJrcwp0aGF0IGNhbiBiZSB1c2VkIHRvIGVuY29kZSBhdHRyaWJ1dGVzLgoKVGhlIGBhZXMoLi4uKWAgZXhwcmVzc2lvbnMgZXN0YWJsaXNoIHRoZSBtYXBwaW5nIGJldHdlZW4gYXR0cmlidXRlcwphbmQgdmlzdWFsIGNoYW5uZWxzLgoKVGhlc2UgaWRlYXMgY2xvc2VseSBtaXJyb3IgdGhlIHN0cnVjdHVyZSBvZiB0aGUgX2dyYW1tYXIgb2YgZ3JhcGhpY3NfCmFzIGltcGxlbWVudGVkIGluIGBnZ3Bsb3RgLgoKPiBNdW56bmVyLCBULiAoMjAxNCksIFtfVmlzdWFsaXphdGlvbiBBbmFseXNpcyBhbmQKPiAgRGVzaWduX10oaHR0cDovL3d3dy5jcy51YmMuY2EvfnRtbS92YWRib29rLyksIENSQyBQcmVzcy4KCj4gV2lsa2luc29uLCBMLiAoMjAwNSksIF9UaGUgR3JhbW1hciBvZiBHcmFwaGljc18sIDJuZCBlZCwgU3ByaW5nZXIuCgoKIyMjIENoYW5uZWxzIGFuZCB0aGVpciBBY2N1cmFjeQoKQSB1c2VmdWwgZGlzdGluY3Rpb24gYW1vbmcgY2hhbm5lbHM6CgoqIF9NYWduaXR1ZGUgY2hhbm5lbHNfIGNhbiByZWZsZWN0IG9yZGVyIGFuZCBudW1lcmljIHZhbHVlcywKICBlLmcuIHBvc2l0aW9uIG9uIGFuIGF4aXMsIGxlbmd0aCwgYXJlYSwgYnJpZ2h0bmVzcy4KCiogX0lkZW50aXR5IGNoYW5uZWxzXyBjYW4gZGlzdGluZ3Vpc2ggZGlmZmVyZW50IHZhbHVlcyBidXQgbm90IHJlZmxlY3QKICBvcmRlciwgZS5nLiBodWUsIHNoYXBlLCBncm91cGluZy4KClNvbWUgY2hhbm5lbHMgYXJlIGJldHRlciBhdCBjb252ZXlpbmcgaW5mb3JtYXRpb24gdGhhbiBvdGhlcnMuCgpNdW56bmVyJ3Mgb3JkZXJpbmcgYnkgYWNjdXJhY3k6Cgp8IE1hZ25pdHVkZSBDaGFubmVscyAoT3JkZXJlZCwgTnVtZXJpY2FsKSB8IElkZW50aXR5IENoYW5uZWxzIChDYXRlZ29yaWNhbCkgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IFBvc2l0aW9uIG9uIGNvbW1vbiBzY2FsZSAgICAgICAgICAgICAgICB8IFNwYXRpYWwgZ3JvdXBpbmcgICAgICAgICAgICAgICAgfAp8IFBvc2l0aW9uIG9uIHVuYWxpZ25lZCBzY2FsZSAgICAgICAgICAgICB8IENvbG9yIGh1ZSAgICAgICAgICAgICAgICAgICAgICAgfAp8IExlbmd0aCAoMUQgc2l6ZSkgICAgICAgICAgICAgICAgICAgICAgICB8IFNoYXBlICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IFRpbHQsIGFuZ2xlICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEFyZWEgKDJEIHNpemUpICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IERlcHRoICgzRCBwb3NpdGlvbikgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IENvbG9yIGx1bWluYW5jZSwgc2F0dXJhdGlvbiAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEN1cnZhdHVyZSwgdm9sdW1lICgzRCBzaXplKSAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKTGluZSB3aWR0aCBpcyBhbm90aGVyIGNoYW5uZWw7IG5vdCBzdXJlIHRoZXJlIGlzIGFncmVlbWVudCBvbiBpdHMKYWNjdXJhY3ksIGJ1dCBpdCBpcyBub3QgaGlnaC4KCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgojIyMjIFZpc3VhbCBEZXNpZ24gSW1wbGljYXRpb25zCgpUcnkgdG8gbWFwIHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMgdG8gdGhlIHN0cm9uZ2VzdCBjaGFubmVscy4KPC9kaXY+CgoKIyMjIENvbG9yCgpDb2xvciBpcyB2ZXJ5IGVmZmVjdGl2ZSB3aGVuIHVzZWQgd2VsbC4KCkJ1dCB1c2luZyBjb2xvciB3ZWxsIGlzIG5vdCBlYXN5LgoKU29tZSBvZiB0aGUgaXNzdWVzOgoKKiBQZXJjZXB0aW9uIGRlcGVuZHMgb24gY29udGV4dC4KCiogU2ltcGxlIGNvbG9yIGFzc2lnbm1lbnRzIG1heSBub3Qgc2VwYXJhdGUgZXF1YWxseSB3ZWxsLgoKKiBFZmZlY3RpdmVuZXNzIG1heSB2YXJ5IHdpdGggdGhlIG1lZGl1bSAoc2NyZWVuLCBwcm9qZWN0b3IsIHByaW50KS4KCiogU29tZSBwZW9wbGUgZG8gbm90IHBlcmNlaXZlIHRoZSBmdWxsIHNwZWN0dXJtIG9mIGNvbG9ycy4KCiogR3JleSBzY2FsZSBwcmludGluZy4KCiogU29tZSBjb2xvcnMgaGF2ZSBjdWx0dXJhbCBzaWduaWZpY2FuY2UuCgoqIEN1bHR1cmFsIHNpZ25pZmljYW5jZSBtYXkgdmFyeSBhbW9uZyBjdWx0dXJlcyBhbmQgd2l0aCB0aW1lLgoKQ29sb3IgcGVyY2VwdGlvbiBpcyByZWxhdGl2ZToKCiFbXShgciBoZXJlX3JlbCgiaW1nL2NoZXNzMS5wbmciKWApCiFbXShgciBoZXJlX3JlbCgiaW1nL2NoZXNzMi5wbmciKWApCgpHcm91cHMgb2YgY29sb3JzIHRoYXQgd29yayB3ZWxsIHRvZ2V0aGVyIGFyZSBjYWxsZWQgX3BhbGV0dGVzXy4KClNvbWUgdG9vbHMgZm9yIHNlbGVjdGluZyBwYWxldHRlcyBpbmNsdWRlOgoKKiBbQ29sb3JCcmV3ZXJdKGh0dHA6Ly9jb2xvcmJyZXdlcjIub3JnKTsgYXZhaWxhYmxlIGluIHRoZQogIGBSQ29sb3JCcmV3ZXJgIHBhY2thZ2UuCgoqIFtIQ0wgV2l6YXJkXShodHRwOi8vd3d3LmhjbHdpemFyZC5vcmcvKTsgYWxzbyBhdmFpbGFibGUgYXMgYGhjbHdpemFyZGAKICBpbiB0aGUgYGNvbG9yc3BhY2VgIHBhY2thZ2UuCgpBIG5vdGUgb24gW3JhaW5ib3cgY29sb3JzXSgKaHR0cHM6Ly9lZWVjb24udWliay5hYy5hdC9+emVpbGVpcy9uZXdzL2VuZHJhaW5ib3cvKS4KCgojIyBBIEdyYW1tYXIgb2YgRGF0YSBNYW5pcHVsYXRpb24KClRoZSBgZHBseXJgIHBhY2thZ2UgcHJvdmlkZXMgYSBsYW5ndWFnZSwgb3IgZ3JhbW1hciwgZm9yIGRhdGEKbWFuaXB1bGF0aW9uLgoKVGhlIGRlc2lnbiBvZiBgZHBseXJgIGlzIHN0cm9uZ2x5IG1vdGl2YXRlZCBieSBTUUwuCgpUaGUgbGFuZ3VhZ2UgY29udGFpbnMgYSBudW1iZXIgb2YgX3ZlcmJzXyB0aGF0IG9wZXJhdGUgb24gdGFibGVzLgoKVGhlIG1vc3QgY29tbW9ubHkgdXNlZCB2ZXJicyBvcGVyYXRlIG9uIGEgc2luZ2xlIGRhdGEgZnJhbWU6CgoqIGBzZWxlY3RgOiBwaWNrIHZhcmlhYmxlcyBieSB0aGVpciBuYW1lcwoKKiBgZmlsdGVyYDogY2hvb3NlIHJvd3MgdGhhdCBzYXRpc2Z5IHNvbWUgY3JpdGVyaWEKCiogYG11dGF0ZWA6IGNyZWF0ZSB0cmFuc2Zvcm1lZCBvciBkZXJpdmVkIHZhcmlhYmxlcwoKKiBgYXJyYW5nZWA6IHJlb3JkZXIgdGhlIHJvd3MKCiogYHN1bW1hcml6ZWA6IGNvbGxhcHNlIHJvd3MgZG93biB0byBzdW1tYXJpZXMKClRoZXJlIGFyZSBhbHNvIGEgbnVtYmVyIG9mIGBqb2luYCB2ZXJicyB0aGF0IG1lcmdlIHNldmVyYWwgZGF0YSBmcmFtZXMKaW50byBvbmUuCgpQYWNrYWdlIGB0aWR5cmAgcHJvdmlkZXMgbW9yZSB2ZXJicywgc3VjaCBhcyBgcGl2b3RfbG9uZ2VyYCBhbmQKYHBpdm90X3dpZGVyYCBmb3IgcmVzaGFwaW5nIGRhdGEgZnJhbWVzLgoKVGhlIHNpbmdsZSB0YWJsZSB2ZXJicyBjYW4gYWxzbyBiZSB1c2VkIHdpdGggYGdyb3VwX2J5YCB0byB3b3JrCnNlcGFyYXRlbHkgb24gZ3JvdXBzIG9mIHJvd3MuCgoKIyMgTW9yZSBFeGFtcGxlcwoKVGhlc2UgZXhhbXBsZXMgc3RhcnQgd2l0aCByYXcgZGF0YSBhcyB5b3UgbWlnaHQgcmVjZWl2ZSBpdCBmcm9tIGEKcmVzZWFyY2hlciwgYW5kIGludm9sdmUgcmVhZGluZyBhbmQgY2xlYW5pbmcgdGhlIGRhdGEuCgpDb21tb24gZGF0YSBmb3JtYXRzIHlvdSBtaWdodCBlbmNvdW50ZXIgaW5jbHVkZQoKKiBbX0NTVl8gKGNvbW1hLXNlcGFyYXRlZAogIHZhbHVlcyldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbW1hLXNlcGFyYXRlZF92YWx1ZXMpIGZpbGVzLgoKKiBUZXh0IGZpbGVzIHVzaW5nIG90aGVyIGRlbGltaXRlcnMsIHN1Y2ggYXMgdGFicyBvciBgfGAgY2hhcmFjdGVycy4KCiogW0pTT04oSmF2YVNjcmlwdCBPYmplY3QKICBOb3RhdGlvbildKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0pTT04pIGZpbGVzLgogIAoKKiBbWE1MIChFeHRlbnNpYmxlIE1hcmt1cAogIExhbmd1YWdlKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvWE1MKSBmaWxlcy4KCiogRXhjZWwgc3ByZWFkc2hlZXRzLgoKVG9vbHMgYXJlIGF2YWlsYWJsZSBmb3IgcmVhZGluZyBkYXRhIGluIHRoZXNlIGZvcm1hdHMgaW50byBSLgoKIyMjIFdpbmQgVHVyYmluZXMgaW4gSW93YQoKVGhlcmUgYXJlIG1hbnkgd2luZCB0dXJiaW5lcyBpbiBJb3dhLgoKRGF0YSBpcyBhdmFpbGFibGUgZnJvbSB0aGUgW1UuUy4gV2luZCBUdXJiaW5lCkRhdGFiYXNlXShodHRwczovL2VlcnNjbWFwLnVzZ3MuZ292L3Vzd3RkYi8pLgoKQSBzbmFwc2hvdCBpcyBhdmFpbGFibGUgaXMgW2hlcmVdKGRhdGEvdXNfd2luZC5jc3YpIGFzIGEgQ1NWIGZpbGUuCgoqIENTViBmaWxlcyBhcmUgYSBjb21tb24gZm9ybSBvZiBkYXRhIGV4Y2hhbmdlLgoKKiBUaGV5IGFyZSBzaW1wbGUgdGV4dCBmaWxlcyB0aGF0IGFyZSBpbnRlbmRlZCB0byBiZSB3cml0dGVuIGFuZCByZWFkCiAgYnkgYSBjb21wdXRlci4KCiogU29tZSBDU1YgZmlsZXMgaW5jbHVkZSBhIGhlYWRlciBhbmQgYSBmb290ZXIgdGhhdCBuZWVkIHRvIGhlIGhhbmRsZWQuCgoqIE9uZSBpc3N1ZSBpcyB0aGF0IGEgY29tbWEgaXNuJ3QgYSBnb29kIHNlcGFyYXRvciBpbiBjb3VudHJpZXMgd2hlcmUKICBpdCBpcyB0aGUgZGVjaW1hbCBzZXBhcmF0b3IhCgoqIEEgQ1NWIGZpbGUgY2FuIGJlIHJlYWQgdXNpbmcgYHJlYWQuY3N2YCBvciBgcmVhZHI6OnJlYWRfY3N2YC4KClJlYWRpbmcgdGhlIHdpbmQgdHVyYmluZSBkYXRhOgoKYGBge3J9CndpbmRfdHVyYmluZXMgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZGF0YS91c193aW5kLmNzdiIpLCBjb21tZW50ID0gIiMiKQpgYGAKU29tZSBkYXRhIGNsZWFuaW5nIGlzIG5lZWRlZC4KCkZvY3VzIG9uIHRoZSB3aW5kIHR1cmJpbmVzIGluIElPV0EgKDE5IGlzIHRoZSBzdGF0ZSBjb21wb25lbnQgb2YgdGhlCltGSVBTIGNvdW50eSBjb2RlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GSVBTX2NvdW50eV9jb2RlKSBmb3IKSW93YSk6CgpgYGB7cn0Kd3RfSUEgPC0gZmlsdGVyKHdpbmRfdHVyYmluZXMsIHRfZmlwcyAlLyUgMTAwMCA9PSAxOSkKYGBgCgpEcm9wIGVudHJpZXMgd2l0aCBtaXNzaW5nIGxvbmdpdHVkZSBvciBsYXRpdHVkZSB2YWx1ZXM6CgpgYGB7cn0Kd3RfSUEgPC0gZmlsdGVyKHd0X0lBLCAhIGlzLm5hKHhsb25nKSwgISBpcy5uYSh5bGF0KSkKYGBgCgpTb21lIG1pc3NpbmcgeWVhciB2YWx1ZXMgYXJlIGVuY29kZWQgYXMgLTk5OTk7IHJlcGxhY2UgdGhlc2Ugd2l0aCBgTkFgOgoKYGBge3J9Cnd0X0lBIDwtIG11dGF0ZSh3dF9JQSwgcF95ZWFyID0gcmVwbGFjZShwX3llYXIsIHBfeWVhciA8IDAsIE5BKSkKYGBgCgpUbyBzaG93IHRoZSBsb2NhdGlvbnMgb2Ygd2luZCB0dXJiaW5lcyBvbiBhIG1hcCwgbG9hZCBzb21lIG1hcCBkYXRhOgoKYGBge3IgaW93YV9zZl9tYXAsIGV2YWwgPSBGQUxTRX0KaW93YV9zZiA8LQogICAgc2Y6OnN0X2FzX3NmKG1hcHM6Om1hcCgiY291bnR5IiwgImlvd2EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBUUlVFKSkKCnAgPC0gZ2dwbG90KCkgKwogICAgZ2VvbV9zZihkYXRhID0gaW93YV9zZikgKwogICAgZ2d0aGVtZXM6OnRoZW1lX21hcCgpCnAKYGBgCgpgYGB7ciBpb3dhX3NmX21hcCwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4fQpgYGAKCkxvY2F0aW9ucyBmb3IgYWxsIHdpbmQgdHVyYmluZXMgaW4gaW93YToKCmBgYHtyIHd0LUlBLWFsbCwgZXZhbCA9IEZBTFNFfQpwICsgZ2VvbV9wb2ludChhZXMoeGxvbmcsIHlsYXQpLAogICAgICAgICAgICAgICBkYXRhID0gd3RfSUEpCmBgYApgYGB7ciB3dC1JQS1hbGwsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gOH0KYGBgCgpVc2luZyBjb2xvciB0byBzaG93IHdoZW4gdGhlIHdpbmQgdHVyYmluZXMgd2VyZSAgYnVpbHQ6CgpgYGB7ciB3dC1JQS1jb2xvciwgZXZhbCA9IEZBTFNFfQp5ZWFyX2JyayA8LSBjKDAsIDIwMDUsIDIwMTAsIDIwMTUsIDIwMjApCnllYXJfbGFiIDwtIGMoImJlZm9yZSAyMDA1IiwKICAgICAgICAgICAgICAiMjAwNS0yMDA5IiwKICAgICAgICAgICAgICAiMjAxMC0yMDE0IiwKICAgICAgICAgICAgICAiMjAxNS0yMDIwIikKd3RfSUEgPC0KICAgIG11dGF0ZSh3dF9JQSwKICAgICAgICAgICB5ZWFyID0gY3V0KHBfeWVhciwKICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfYnJrLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0geWVhcl9sYWIsCiAgICAgICAgICAgICAgICAgICAgICByaWdodCA9IEZBTFNFKSkKcCArIGdlb21fcG9pbnQoYWVzKHhsb25nLAogICAgICAgICAgICAgICAgICAgeWxhdCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0geWVhciksCiAgICAgICAgICAgICAgIGRhdGEgPSB3dF9JQSwKICAgICAgICAgICAgICAgc2l6ZSA9IDMpCmBgYApgYGB7ciB3dC1JQS1jb2xvciwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4fQpgYGAKCmBgYHtyIGV2YWwgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKcCA8LSBnZ3Bsb3QoKSArIGdlb21fc2YoZGF0YSA9IGlvd2Ffc2YpICsgZ2d0aGVtZXM6OnRoZW1lX21hcCgpCnAgKyBnZW9tX3BvaW50KGFlcyh4bG9uZywgeWxhdCksIGRhdGEgPSB3dF9JQSkKCnd0X0lBX3NmIDwtIHNmOjpzdF9hc19zZih3dF9JQSwgY29vcmRzID0gYygieGxvbmciLCAieWxhdCIpLCBjcnMgPSA0MzI2KQoKcCArIGdlb21fc2YoZGF0YSA9IGZpbHRlcih3dF9JQV9zZiwgeWVhciA8PSAyMDIwKSkKCmxpYnJhcnkoZ2dhbmltYXRlKQpwYSA8LSBwICsgZ2VvbV9zZihkYXRhID0gd3RfSUFfc2YpICsKICAgIHRyYW5zaXRpb25fbWFudWFsKHllYXIsIGN1bXVsYXRpdmUgPSBUUlVFKSArCiAgICBsYWJzKHRpdGxlID0gIldpbmQgdHVyYmluZXMgaW4gSW93YSIsCiAgICAgICAgIHN1YnRpdGxlID0gIlllYXIgPSB7Y3VycmVudF9mcmFtZX0iKQphbmltX3NhdmUoImZvby5naWYiLCBhbmltYXRlKHBhLCBmcHMgPSAxMCwgbmZyYW1lcyA9IDEwMCkpCmBgYAoKIyMjIENhbmNlciBNYXAKClRoZSB3ZWJzaXRlIDxodHRwOi8vd3d3LmNhbmNlci1yYXRlcy5pbmZvL2lhPiBwcm92aWRlcyBkYXRhIG9uCmNhbmNlciBpbmNpZGVuY2UgZm9yIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBjYW5jZXJzIGluIElvd2EuCgo8IS0tICMgbm9saW50IHN0YXJ0IC0tPgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KY2FuY2VyX2RhdGFfZmlsZSA8LSBoZXJlOjpoZXJlKCJkYXRhL0ludmFzaXZlLUNhbmNlci1JbmNpZGVuY2UtUmF0ZXMtYnktQ291bnR5LWluLUlvd2EtTHVuZy1hbmQtQnJvbmNodXMtMjAxMS5jc3YiKQpgYGAKCjwhLS0gIyBub2xpbnQgZW5kIC0tPgoKVGhlIGRhdGEgZm9yIGx1bmcgYW5kIGJyb25jaHVzIGNhbmNlciBpbiAyMDExIGFyZSBhdmFpbGFibGUgaW4gYSBbY3N2CmZpbGVdKGByIGNhbmNlcl9kYXRhX2ZpbGVgKSBpbiB0aGUgcHJvamVjdC4KCldlIGNhbiByZWFkIHRoZSBmaWxlIHdpdGggYHJlYWRfY3N2YCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCgpMb29raW5nIGF0IHRoZSBmaWxlIHNob3dzIHNvbWUgdGhpbmdzIHRoYXQgbmVlZCB0byBiZSBjbGVhbmVkIHVwOgoKKiBUd28gaGVhZGVyIGxpbmVzIGF0IHRoZSBiZWdpbm5pbmcuCgoqIFNvbWUgZm9vdGVyIGxpbmVzLgoKKiBTb21lIHZhbHVlcyBjb2RlcyBhcyBgfmAuCgpUaGUgaGVhZGVyIGNhbiBiZSBoYW5kbGVkIGJ5IHVzaW5nIGBza2lwID0gMmAgaW4gdGhlIGByZWFkX2NzdmAgY2FsbDoKCjwhLS0gIyBub2xpbnQgc3RhcnQgLS0+CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpmbmFtZSA8LSBoZXJlOjpoZXJlKCJkYXRhL0ludmFzaXZlLUNhbmNlci1JbmNpZGVuY2UtUmF0ZXMtYnktQ291bnR5LWluLUlvd2EtTHVuZy1hbmQtQnJvbmNodXMtMjAxMS5jc3YiKQpkIDwtIHJlYWRfY3N2KGZuYW1lLCBza2lwID0gMikKaGVhZChkKQpgYGAKCjwhLS0gIyBub2xpbnQgZW5kIC0tPgoKTGV0J3MgZm9jdXMgb24gYSBmZXcgdmFyaWFibGVzIGFuZCBnaXZlIHRoZW0gbW9yZSBjb252ZW5pZW50IG5hbWVzOgoKYGBge3J9CmQgPC0gc2VsZWN0KGQsIGNvdW50eSA9IDEsIHBvcHVsYXRpb24gPSAyLCBjb3VudCA9IDMpCmBgYAoKVGhlIGZvb3RlciBuZWVkcyB0byBiZSByZW1vdmVkOgoKYGBge3J9CnRhaWwoZCkKYGBgCgpPbmUgd2F5IHRvIHJlbW92ZSB0aGUgZm9vdGVyOgoKYGBge3J9CmQgPC0gZmlsdGVyKGQsICEgaXMubmEocG9wdWxhdGlvbikpCmQgPC0gZmlsdGVyKGQsIGNvdW50eSAhPSAiU1RBVEUiKQpgYGAKCkNoYW5naW5nIGBjb3VudGAgdG8gbnVtZXJpYyBjaGFuZ2VzIHRoZSBgfmAgZW50cmllcyB0byBtaXNzaW5nIHZhbHVlcwooYE5BYCkgdmFsdWVzOgoKYGBge3J9CmQgPC0gbXV0YXRlKGQsIGNvdW50ID0gYXMubnVtZXJpYyhjb3VudCkpCmBgYAoKSW4gdGhpcyBjYXNlIHRoZXJlIGFyZSBubyB6ZXJvIGNhc2UgdmFsdWVzOyB0d28gd2F5cyB0byBjaGVjazoKCmBgYHtyfQpjb3VudChkLCBjb3VudCA9PSAwKQpgYGAKYGBge3J9CmFueShkJGNvdW50ID09IDAsIG5hLnJtID0gVFJVRSkKYGBgCgpJdCBfbWlnaHRfIGJlIHJlYXNvbmFibGUgdG8gYXNzdW1lIHRoZXNlIHZhbHVlcyB3aGVyZSB6ZXJvLCBzbyByZXBsYWNlCnRoZW0gd2l0aCB6ZXJvczoKCmBgYHtyfQpkIDwtIHJlcGxhY2VfbmEoZCwgbGlzdChjb3VudCA9IDApKQpgYGAKCkEgX2Nob3JvcGxldGggbWFwXyB1c2VzIGNvbG9yIG9yIHNoYWRpbmcgdG8gcmVwcmVzZW50IHZhbHVlcyBtZWFzdXJlZApmb3IgZGlmZmVyZW50IGdlb2dyYXBoaWMgcmVnaW9ucy4KCldlIHdpbGwgbmVlZCB0byBtZXJnZSwgb3IgX2xlZnQgam9pbl8sIHRoZSBjYW5jZXIgZGF0YSB3aXRoIHRoZSBtYXAKZGF0ZSB3ZSBsb2FkZWQgZm9yIHRoZSB3aW5kIHR1cmJpbmUgbWFwLgoKVGhpcyByZXF1aXJlcyBhIF9rZXlfIG9uIHdoaWNoIHRvIG1hdGNoIHRoZSByZWNvcmRzIGluIHRoZSBjYW5jZXIgZGF0YQphbmQgdGhlIG1hcCBkYXRhLgoKRm9yIElvd2EgdGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBjb3VudHkgbmFtZSwgYnV0IHNvbWUgY2FyZSBpcyBuZWVkZWQ6CgpgYGB7cn0KZCRjb3VudHlbMV0KaW93YV9zZiRJRFsxXQpgYGAKCkZpeGluZyBjYXNlIGRpZmZlcmVuY2VzIGFuZCBkcm9wcGluZyB0aGUgYGlvd2EsYCBwcmVmaXg6CgpgYGB7cn0KZCA8LSBtdXRhdGUoZCwgY25hbWUgPSBjb3VudHksIGNvdW50eSA9IHRvbG93ZXIoY291bnR5KSkKaW93YV9zZiA8LSBtdXRhdGUoaW93YV9zZiwgSUQgPSBzdWIoImlvd2EsIiwgIiIsIElEKSkKaW93YV9zZiA8LSByZW5hbWUoaW93YV9zZiwgY291bnR5ID0gSUQpCmBgYAoKU3RpbGwgbm90IHF1aXRlIHRoZXJlOgoKYGBge3J9CnNldGRpZmYoZCRjb3VudHksIGlvd2Ffc2YkY291bnR5KQpzZXRkaWZmKGlvd2Ffc2YkY291bnR5LCBkJGNvdW50eSkKYGBgCgpEcm9wIHRoZSBhcG9zdHJvcGhlIGluIE8nQnJpZW46CgpgYGB7cn0KZCA8LSBtdXRhdGUoZCwgY291bnR5ID0gc3ViKCInIiwgIiIsIGNvdW50eSkpCgpzZXRkaWZmKGQkY291bnR5LCBpb3dhX3NmJGNvdW50eSkKc2V0ZGlmZihpb3dhX3NmJGNvdW50eSwgZCRjb3VudHkpCmBgYAoKRGVmaW5lIGByYXRlMUtgIHZhcmlhYmxlIGFzIHRoZSBudW1iZXIgb2YgY2FzZXMgcGVyIDEwMDAgaW5oYWJpdGFudHMKYW5kIGxlZnQgam9pbiB0aGUgY2FuY2VyIGRhdGEgdG8gdGhlIG1hcCBkYXRhOgoKYGBge3J9CmQgPC0gbXV0YXRlKGQsIHJhdGUxSyA9IDEwMDAgKiAoY291bnQgLyBwb3B1bGF0aW9uKSkKbWQgPC0gbGVmdF9qb2luKGlvd2Ffc2YsIGQsICJjb3VudHkiKQpoZWFkKG1kKQpgYGAKCkEgc2ltcGxlIG1hcDoKCmBgYHtyIGNhbmNlci1tYXAtMSwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobWQpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSByYXRlMUspKQpgYGAKYGBge3IgY2FuY2VyLW1hcC0xLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDh9CmBgYAoKQW4gaW1wcm92ZWQgdmVyc2lvbjoKCmBgYHtyIGNhbmNlci1tYXAtMiwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHZpcmlkaXMpCmdncGxvdChtZCkgKwogICAgZ2VvbV9zZihhZXMoZmlsbCA9IHJhdGUxSyksCiAgICAgICAgICAgIGNvbG9yID0gImdyZXkiKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJSYXRlIHBlciAxMDAwIikgKwogICAgdGhlbWVfbWFwKCkKYGBgCmBgYHtyIGNhbmNlci1tYXAtMiwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBtZXNzYWdlID0gRkFMU0V9CmBgYAoKQSBzaW1wbGUgaW50ZXJhY3RpdmUgdmVyc2lvbiB1c2luZyBbYHBsb3RseWBdKGh0dHBzOi8vcGxvdC5seS9yLyk6CgpgYGB7ciBjYW5jZXItbWFwLXBsb3RseSwgZXZhbCA9IEZBTFNFfQptZGwgPC0gbXV0YXRlKG1kLAogICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoY25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChyYXRlMUssIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcbiIpKQpwIDwtIGdncGxvdChtZGwpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSByYXRlMUssCiAgICAgICAgICAgICAgICB0ZXh0ID0gbGFiZWwpLAogICAgICAgICAgICBjb2xvciA9ICJncmV5IikgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWUgPSAiUmF0ZSBwZXIgMTAwMCIpICsKICAgIHRoZW1lX21hcCgpCgpwbG90bHk6OmdncGxvdGx5KHAsIHRvb2x0aXAgPSAidGV4dCIpCmBgYApgYGB7ciBjYW5jZXItbWFwLXBsb3RseSwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4fQpgYGAKClRoZSBbYGxlYWZsZXRgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvKSBwYWNrYWdlIHN1cHBvcnRzCm1vcmUgc29waGlzdGljYXRlZCBpbnRlcmFjdGl2ZSBtYXBzOgoKPCEtLSBodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC9sZWdlbmRzLmh0bWwtLT4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpsaWJyYXJ5KGxlYWZsZXQpCnBhbCA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJ2aXJpZGlzIiwgZG9tYWluID0gbWQkcmF0ZTFLKQpsYWIgPC0gbGFwcGx5KHBhc3RlMChtZCRjbmFtZSwgIjxCUj4iLAogICAgICAgICAgICAgICAgICAgICAiUmF0ZTogIiwgcm91bmQobWQkcmF0ZTFLLCAxKSwgIjxCUj4iLAogICAgICAgICAgICAgICAgICAgICAiUG9wOiAiLCBzY2FsZXM6OmNvbW1hKG1kJHBvcHVsYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjdXJhY3kgPSAxKSksCiAgICAgICAgICAgICAgaHRtbHRvb2xzOjpIVE1MKQpsZWFmbGV0KHNmOjpzdF90cmFuc2Zvcm0obWQsIDQzMjYpKSAlPiUKICAgIGFkZFBvbHlnb25zKHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgICBjb2xvciA9ICJncmV5IiwKICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH4gcGFsKHJhdGUxSyksCiAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgICBoaWdobGlnaHRPcHRpb25zID0gaGlnaGxpZ2h0T3B0aW9ucyhjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJpbmdUb0Zyb250ID0gVFJVRSksCiAgICAgICAgICAgICAgICBsYWJlbCA9IGxhYikgJT4lCiAgICBhZGRMZWdlbmQocGFsID0gcGFsLCB2YWx1ZXMgPSB+IHJhdGUxSywgb3BhY2l0eSA9IDEpCmBgYAoKCiMjIyBVbmVtcGxveW1lbnQgTWFwCgpbTG9jYWwgQXJlYSBVbmVtcGxveW1lbnQgU3RhdGlzdGljcyBwYWdlXShodHRwczovL3d3dy5ibHMuZ292L2xhdS8pCmZyb20gdGhlIEJ1cmVhdSBvZiBMYWJvciBTdGF0aXN0aWNzIG1ha2VzIGF2YWlsYWJsZSBjb3VudHktbGV2ZWwKbW9udGhseSB1bmVtcGxveW1lbnQgZGF0YSBmb3IgYSAxNC1tb250aCB3aW5kb3cuCgpUaGUgZmlsZSBmb3IgRmVicnVhcnkgMjAyMCB0aHJvdWdoIE1hcmNoIDIwMjEgaXMgYXZhaWxhYmxlIGlzCmF2YWlsYWJsZSBhdAo8aHR0cDovL3d3dy5zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL2xhdXMvbGF1Y250eWN1cjE0LTIwMjAudHh0PiBhbmQKaW4gdGhlIHByb2plY3QgZGF0YSBmb2xkZXIuCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KVGhpcyBmaWxlIGlzIGEgdGV4dCBmaWxlIGJ1dCB1c2VzIGEgbm9uLXN0YW5kYXJkIHNlcGFyYXRvci4KCkl0IGlzIGRlc2lnbmVkIGZvciBodW1hbiByZWFkYWJpbGl0eSBhbmQgdXNlcwphIGNvbW1hIGFzIGEgX3Rob3VzYW5kcyBzZXBhcmF0b3JfIG9yIF9ncm91cGluZyBtYXJrXy4KCkl0IGFsc28gaW5jbHVkZXMgaGVhZGVyIGFuZCBmb290ZXIgaW5mb3JtYXRpb24uCgpJdCBpcyBzdGlsbCByZWFzb25hYmx5IGVhc3kgdG8gcmVhZCBpbi4KPC9kaXY+CgpPbmUgd2F5IHRvIHJlYWQgdGhlIGRhdGEgaW50byBSIGlzOgoKYGBge3J9CmxhdXNVUkwgPC0gaGVyZTo6aGVyZSgiZGF0YS9sYXVjbnR5Y3VyMTQtMjAyMC50eHQiKQpsYXVzVVMgPC0gcmVhZC50YWJsZShsYXVzVVJMLAogICAgICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJMQVVTQXJlYUNvZGUiLCAiU3RhdGUiLCAiQ291bnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGl0bGUiLCAiUGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFib3JGb3JjZSIsICJFbXBsb3llZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVuZW1wbG95ZWQiLCAiVW5lbXBSYXRlIiksCiAgICAgICAgICAgICAgICAgICAgIHF1b3RlID0gJyInLCBzZXAgPSAifCIsIHNraXAgPSA2LAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFRSVUUpCmZvb3RzdGFydCA8LSBncmVwKCItLS0tLS0iLCBsYXVzVVMkTEFVU0FyZWFDb2RlKQpsYXVzVVMgPC0gbGF1c1VTWzEgOiAoZm9vdHN0YXJ0IC0gMSksIF0KYGBgCgpJdCBtYXkgYmUgdXNlZnVsIHRvIGJlIGFibGUgdG8gYWNjZXNzIHRoZSBjb3VudHkgbmFtZSBhbmQgc3RhdGUgbmFtZQpzZXBhcmF0ZWx5OgoKYGBge3J9CmxhdXNVUyA8LSBzZXBhcmF0ZShsYXVzVVMsIFRpdGxlLCBjKCJjbmFtZSIsICJzY29kZSIpLAogICAgICAgICAgICAgICAgICAgc2VwID0gIiwgIiwgZmlsbCA9ICJyaWdodCIpCmBgYAoKQ2hlY2sgdGhlIHZhcmlhYmxlIHR5cGVzOgoKYGBge3J9CnNhcHBseShsYXVzVVMsIGNsYXNzKQpgYGAKClRoZSBgVW5lbXBSYXRlYCB2YXJpYWJsZSBpcyByZWFkIGFzIGNoYXJhY3RlciBkYXRhIGJlY2F1c2Ugb2YgbWlzc2luZwp2YWx1ZSBlbmNvZGluZywgc28gbmVlZHMgdG8gYmUgY29udmVydGVkIHRvIG51bWVyaWM6CgpgYGB7cn0KbGF1c1VTIDwtIG11dGF0ZShsYXVzVVMsIFVuZW1wUmF0ZSA9IGFzLm51bWVyaWMoVW5lbXBSYXRlKSkKYGBgCgpDaGVjayBmb3IgbWlzc2luZyB2YWx1ZXM6CgpgYGB7cn0Kc2VsZWN0X2lmKGxhdXNVUywgYW55TkEpICU+JSBuYW1lcygpCmBgYAoKVGhlIHN0YXRlIGNvZGUgaXMgbWlzc2luZyBmb3IgdGhlIERpc3RyaWN0IG9mIENvbHVtYmlhOgoKYGBge3J9CnNlbGVjdChsYXVzVVMsIGNuYW1lLCBzY29kZSkgJT4lCiAgICBmaWx0ZXIoaXMubmEoc2NvZGUpKSAlPiUKICAgIHVuaXF1ZSgpCmBgYAoKPCEtLQpNaXNzaW5nIHZhbHVlcyBmb3IgYFVuZW1wUmF0ZWAgYXJlIGFsbCBmb3IgUHVlcnRvIFJpY28gYW5kIFNlcHRlbWJlcgoyMDE3LiBIdXJyaWNhbmUgTWFyaWEgbWFkZSBsYW5kZmFsbCBvbiBTZXB0ZW1iZXIgMjAuIC0tPgoKTWFyY2ggYW5kIEFwcmlsIDIwMjAgbnVtYmVycyB3ZXJlIG5vdCBhdmFpbGFibGUgZm9yIFB1ZXJ0byBSaWNvOgoKYGBge3J9CnNlbGVjdChsYXVzVVMsIHNjb2RlLCBQZXJpb2QsIFVuZW1wUmF0ZSkgJT4lCiAgICBmaWx0ZXIoaXMubmEoVW5lbXBSYXRlKSkgJT4lCiAgICB1bmlxdWUoKQpgYGAKClRvIGNvbXB1dGUgdGhlIG5hdGlvbmFsIG1vbnRobHkgdW5lbXBsb3ltZW50IHJhdGVzIG92ZXIgdGhpcyBwZXJpb2Qgd2UKbmVlZCBzb21lIG1vcmUgZGF0YSBjbGVhbmluZzoKCmBgYHtyfQpsYXVzVVMgPC0gbXV0YXRlKGxhdXNVUywKICAgICAgICAgICAgICAgICBQZXJpb2QgPSBmY3RfaW5vcmRlcihQZXJpb2QpLAogICAgICAgICAgICAgICAgIExhYm9yRm9yY2UgPSBhcy5udW1lcmljKGdzdWIoIiwiLCAiIiwgTGFib3JGb3JjZSkpLAogICAgICAgICAgICAgICAgIFVuZW1wbG95ZWQgPSBhcy5udW1lcmljKGdzdWIoIiwiLCAiIiwgVW5lbXBsb3llZCkpKQpgYGAKClVuZW1wbG95bWVudCBkdXJpbmcgdGhpcyBwZXJpb2Qgd2FzIGFmZmVjdGVkIHNpZ25pZmljYW50bHkgYnkgdGhlCkNPVklELTE5IHBhbmRlbWljLgoKQSBwbG90IHNob3dzIGEgbGFyZ2Ugc3Bpa2UgaW4gQXByaWwgMjAyMDoKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KZ3JvdXBfYnkobGF1c1VTLCBQZXJpb2QpICU+JQogICAgc3VtbWFyaXplKFVuZW1wbG95ZWQgPSBzdW0oVW5lbXBsb3llZCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICBMYWJvckZvcmNlID0gc3VtKExhYm9yRm9yY2UsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgVW5lbXBSYXRlID0gMTAwICogKFVuZW1wbG95ZWQgLyBMYWJvckZvcmNlKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKFBlcmlvZCwgVW5lbXBSYXRlLCBncm91cCA9IDEpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKCkEgY2hvcm9wbGV0aCBtYXAgY2FuIGJlIHVzZWQgdG8gbG9vayBhdCBob3cgdGhlIGltcGFjdCB3YXMgZGlzdHJpYnV0ZWQKYWNyb3NzIHRoZSBjb3VudHJ5LgoKVG8gc2hvdyB1bmVtcGxveW1lbnQgcmF0ZXMgb24gYSBtYXAgd2UgbmVlZCB0byBtZXJnZSB0aGUgdW5lbXBsb3ltZW50IGRhdGEKd2l0aCBtYXAgZGF0YS4KClRvIG1hdGNoIGNvdW50eSB1bmVtcGxveW1lbnQgZGF0YSBhbmQgY291bnR5IHNoYXBlIGRhdGEgaXQgaXMgc2FmZXIgdG8KdXNlIHRoZSBudW1lcmljIFtGSVBTIGNvdW50eQpjb2RlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GSVBTX2NvdW50eV9jb2RlKS4gVGhpcyBjYW4gYmUKYWRkZWQgd2l0aAoKYGBge3J9CmxhdXNVUyA8LSBtdXRhdGUobGF1c1VTLCBmaXBzID0gU3RhdGUgKiAxMDAwICsgQ291bnR5KQpgYGAKClNoYXBlIGRhdGEgZm9yIFVTIGNvdW50aWVzIGNhbiBiZSBvYnRhaW5lZCBmcm9tIGEgbnVtYmVyIG9mIHNvdXJjZXMgaW4gYQpudW1iZXIgb2YgZGlmZmVyZW50IGZvcm1hdHMuCgpIZXJlIGlzIG9uZSBhcHByb2FjaDoKCmBgYHtyfQpjb3VudGllc19zZiA8LSBzZjo6c3RfYXNfc2YobWFwczo6bWFwKCJjb3VudHkiLCBwbG90ID0gRkFMU0UsIGZpbGwgPSBUUlVFKSkKY291bnR5LmZpcHMgPC0KICAgIG11dGF0ZShtYXBzOjpjb3VudHkuZmlwcywgcG9seW5hbWUgPSBzdWIoIjouKiIsICIiLCBwb2x5bmFtZSkpICU+JQogICAgdW5pcXVlKCkKY291bnRpZXNfc2YgPC0gbGVmdF9qb2luKGNvdW50aWVzX3NmLCBjb3VudHkuZmlwcywgYygiSUQiID0gInBvbHluYW1lIikpCnN0YXRlc19zZiA8LSBzZjo6c3RfYXNfc2YobWFwczo6bWFwKCJzdGF0ZSIsIHBsb3QgPSBGQUxTRSwgZmlsbCA9IFRSVUUpKQpgYGAKClNvbWUgc3VtbWFyaWVzIG92ZXIgdGhlIHBlcmlvZCBjYW4gYmUgY29tcHV0ZWQgYXMKCmBgYHtyfQpzdW1tYXJ5VVMgPC0gZ3JvdXBfYnkobGF1c1VTLCBDb3VudHksIFN0YXRlLCBmaXBzKSAlPiUKICAgIHN1bW1hcml6ZShhdmdfdW5lbXAgPSBtZWFuKFVuZW1wUmF0ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICBtYXhfdW5lbXAgPSBtYXgoVW5lbXBSYXRlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIGFwcl91bmVtcCA9IFVuZW1wUmF0ZVtQZXJpb2QgPT0gIkFwci0yMCJdKSAlPiUKICAgIHVuZ3JvdXAoKQpoZWFkKHN1bW1hcnlVUykKYGBgCgpBIGNob3JvcGxldGggbWFwIG9mIHRoZSBBcHJpbCAyMDIwIHVuZW1wbG95bWVudCByYXRlczoKCmBgYHtyLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmxlZnRfam9pbihjb3VudGllc19zZiwgc3VtbWFyeVVTLCAiZmlwcyIpICU+JQogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9zZihhZXMoZmlsbCA9IGFwcl91bmVtcCkpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lID0gIlJhdGUiLCBuYS52YWx1ZSA9ICJyZWQiKSArCiAgICB0aGVtZV9tYXAoKSArCiAgICBnZW9tX3NmKGRhdGEgPSBzdGF0ZXNfc2YsIGNvbCA9ICJncmV5IiwgZmlsbCA9IE5BKQpgYGAKClVzaW5nIGEgdmVyeSB2aXNpYmxlIGNvbG9yIGZvciBtaXNzaW5nIGRhdGEgaXMgdXNlZnVsLCBhdCBsZWFzdCBkdXJpbmcKZXhwbG9yYXRpb24uCgpgYW50aV9qb2luYCBjYW4gc2hvdyB0aGUgY291bnR5IGdlb21ldHJ5IHRoYXQgZG9lcyBub3QgaGF2ZSBhbiBlbnRyeQppbiB0aGUgdW5lbXBsb3ltZW50IGRhdGE6CgpgYGB7cn0KYW50aV9qb2luKGNvdW50aWVzX3NmLCBzdW1tYXJ5VVMsICJmaXBzIikKYGBgCgpTaGFubm9uIENvdW50eSwgU0QgKEZJUFMgNDYxMTMpLCB3YXMgcmVuYW1lZCB0byBbT2dsYWxhIExha290YSBDb3VudHldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL09nbGFsYV9MYWtvdGFfQ291bnR5LF9Tb3V0aF9EYWtvdGEpIGluIEp1bmUgMjAxNSBhbmQgZ2l2ZW4gYSBuZXcgRklQUyBjb2RlLCA0NjEwMi4KClRoZSBnZW9tZXRyeSBkYXRhIHRhYmxlIG5lZWRzIHRvIGJlIHVwZGF0ZWQ6CgpgYGB7cn0KY291bnRpZXNfc2YgPC0gbXV0YXRlKGNvdW50aWVzX3NmLCBmaXBzID0gcmVwbGFjZShmaXBzLCBmaXBzID09IDQ2MTEzLCA0NjEwMikpCmBgYAoKV2l0aCB0aGUgdXBkYXRlZCBkYXRhIHRoZSBtYXAgaXMgbm93IGNvbXBsZXRlOgoKYGBge3IsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KbGVmdF9qb2luKGNvdW50aWVzX3NmLCBzdW1tYXJ5VVMsICJmaXBzIikgJT4lCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGFlcyhmaWxsID0gYXByX3VuZW1wKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWUgPSAiUmF0ZSIsIG5hLnZhbHVlID0gInJlZCIpICsKICAgIHRoZW1lX21hcCgpICsKICAgIGdlb21fc2YoZGF0YSA9IHN0YXRlc19zZiwgY29sID0gImdyZXkiLCBmaWxsID0gTkEpCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZXZhbCA9IEZBTFNFfQpnZ3BvbHkyc2YgPC0gZnVuY3Rpb24ocG9seSwgY29vcmRzID0gYygibG9uZyIsICJsYXQiKSwKICAgICAgICAgICAgICAgICAgICAgIGlkID0gImdyb3VwIiwgcmVnaW9uID0gInJlZ2lvbiIsIGNycyA9IDQzMjYpIHsKICAgIHNmOjpzdF9hc19zZihwb2x5LCBjb29yZHMgPSBjb29yZHMsIGNycyA9IGNycykgJT4lCiAgICBncm91cF9ieSghISBhcy5uYW1lKGlkKSwgISEgYXMubmFtZShyZWdpb24pKSAlPiUKICAgIHN1bW1hcml6ZShkb191bmlvbiA9IEZBTFNFKSAlPiUKICAgIHNmOjpzdF9jYXN0KCJQT0xZR09OIikgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBncm91cF9ieSghISBhcy5uYW1lKHJlZ2lvbikpICU+JQogICAgc3VtbWFyaXplKGRvX3VuaW9uID0gRkFMU0UpICU+JQogICAgdW5ncm91cCgpCn0KbV9zZiA8LSBnZ3BvbHkyc2Yoc29jdml6Ojpjb3VudHlfbWFwLCBjKCJsb25nIiwgImxhdCIpLCAiZ3JvdXAiLCAiaWQiKQptX3NmIDwtIG11dGF0ZShtX3NmLCBmaXBzID0gYXMubnVtZXJpYyhpZCkpCm1fc2YgPC0gbXV0YXRlKG1fc2YsIGZpcHMgPSByZXBsYWNlKGZpcHMsIGZpcHMgPT0gNDYxMTMsIDQ2MTAyKSkKZ2dwbG90KG1fc2YpICsgZ2VvbV9zZigpCmF1IDwtIGdyb3VwX2J5KGxhdXNVUywgZmlwcykgJT4lCiAgICBzdW1tYXJpemUoYXZnX3VyID0gbWVhbihVbmVtcFJhdGUsIG5hLnJtID0gVFJVRSkpCm11IDwtIGdyb3VwX2J5KGxhdXNVUywgZmlwcykgJT4lCiAgICBzdW1tYXJpemUobWF4X3VyID0gbWF4KFVuZW1wUmF0ZSwgbmEucm0gPSBUUlVFKSkKZGEgPC0gbGVmdF9qb2luKG1fc2YsIGF1LCAiZmlwcyIpCmRtIDwtIGxlZnRfam9pbihtX3NmLCBtdSwgImZpcHMiKQpnZ3Bsb3QoZGEsIGFlcyhmaWxsID0gYXZnX3VyKSkgKwogICAgZ2VvbV9zZihzaXplID0gMC4xKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJSYXRlIiwgbmEudmFsdWUgPSAicmVkIikKZ2dwbG90KGRtLCBhZXMoZmlsbCA9IG1heF91cikpICsKICAgIGdlb21fc2Yoc2l6ZSA9IDAuMSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWUgPSAiUmF0ZSIsIG5hLnZhbHVlID0gInJlZCIpCmdncGxvdChsZWZ0X2pvaW4obV9zZiwgZmlsdGVyKGxhdXNVUywgUGVyaW9kID09ICJBcHItMjAiKSwgImZpcHMiKSwKICAgICAgIGFlcyhmaWxsID0gVW5lbXBSYXRlKSkgKwogICAgZ2VvbV9zZihzaXplID0gMC4xKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJSYXRlIiwgbmEudmFsdWUgPSAicmVkIikKYGBgCgojIyMgR2FwbWluZGVyIENoaWxkaG9vZCBNb3J0YWxpdHkgRGF0YQoKVGhlIGBnYXBtaW5kZXJgIHBhY2thZ2UgcHJvdmlkZXMgYSBzdWJzZXQgb2YgdGhlIGRhdGEgZnJvbSB0aGUKW0dhcG1pbmRlcl0oaHR0cDovL3d3dy5nYXBtaW5kZXIub3JnLykgd2ViIHNpdGUuCgpBZGRpdGlvbmFsIGRhdGEgc2V0cyBhcmUgW2F2YWlsYWJsZV0oaHR0cDovL3d3dy5nYXBtaW5kZXIub3JnL2RhdGEvKS4KCiogQSBkYXRhIHNldCBvbiBjaGlsZGhvb2QgbW9ydGFsaXR5IGlzIGF2YWlsYWJsZSBsb2NhbGx5IGFzIGEgW2NzdgogIGZpbGVdKGh0dHA6Ly9ob21lcGFnZS5zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL2dhcG1pbmRlci11bmRlcjVtb3J0YWxpdHkuY3N2KQogIG9yIGFuIFtFeGNlbAogIGZpbGVdKGh0dHA6Ly9ob21lcGFnZS5zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL2dhcG1pbmRlci11bmRlcjVtb3J0YWxpdHkueGxzeCkuCgoqIFRoZSBFeGNlbCBmaWxlIGlzIGFsc28gYXZhaWxhYmxlIGluIHRoZSBwcm9qZWN0IGRhdGEgZm9sZGVyLgoKKiBUaGUgbnVtYmVycyByZXByZXNlbnQgbnVtYmVyIG9mIGRlYXRocyB3aXRoaW4gdGhlIGZpcnN0IGZpdmUgeWVhcnMKICBwZXIgMTAwMCBiaXJ0aHMuCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KTWFueSByZXNlYXJjaGVycyBsaWtlIHRvIG1hbmFnZSB0aGVpciBkYXRhIGluIGEgc3ByZWFkc2hlZXQuCgoqIEJlaW5nIGFibGUgdG8gcmVhZCBzdWNoIGEgc2hlZXQgZGlyZWN0bHkgZ3JlYXRseSBoZWxwcyBrZWVwaW5nIHRoZQogIHdvcmtmbG93IHJlcHJvZHVjaWJsZS4KCiogTWFueSBzcHJlYWRzaGVldHMgY29udGFpbiBoZWFkZXIsIGZvb3RlcnMsIGFuZCBvdGhlciBhbm5vdGF0aW9ucyB0bwogIGFpZCBhIGh1bWFuIHZpZXdlci4KCiogQXMgbG9uZyBhcyB0aGUgZGF0YSBhcmUgaW4gYSByZWN0YW5ndWxhciByZWdpb24gaXQgaXMgdXN1YWxseSBub3QKICBoYXJkIHRvIGV4dHJhY3QgdGhlbSBwcm9ncmFtbWF0aWNhbGx5Lgo8L2Rpdj4KCkxvYWRpbmcgdGhlIGRhdGE6CgpgYGB7cn0KbGlicmFyeShyZWFkeGwpCmdjbSA8LSByZWFkX2V4Y2VsKGhlcmU6OmhlcmUoImRhdGEvZ2FwbWluZGVyLXVuZGVyNW1vcnRhbGl0eS54bHN4IikpCmBgYAoKQSBmaXJzdCBsb29rOgoKYGBge3J9CmhlYWQoZ2NtLCAzKQpgYGAKClRoaXMgZGF0YSBzZXQgaXMgaW4gX3dpZGVfIGZvcm1hdCwgd2l0aCBvbmUgY29sdW1uIHBlciB5ZWFyLgoKQSBfbG9uZ18gdmVyc2lvbiB3aXRoIGEgeWVhciBhbmQgYSB2YWx1ZSBjb2x1bW4gaXMgdXNlZnVsIGZvciB3b3JraW5nCndpdGggYGdncGxvdGAuCgpBIGJldHRlciBmaXJzdCB2YXJpYWJsZSBuYW1lOgoKYGBge3J9Cm5hbWVzKGdjbSlbMV0gPC0gImNvdW50cnkiCmBgYAoKQ29udmVydCB0byBsb25nIGZvcm1hdDoKCmBgYHtyfQp0Z2NtIDwtCiAgICBwaXZvdF9sb25nZXIoZ2NtLCAtMSwgbmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJ1NW1vcnQiKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyh5ZWFyKSkKaGVhZCh0Z2NtLCAzKQpgYGAKClNvbWUgZXhwbG9yYXRpb25zOgoKYGBge3IgdTUtMSwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdCh0Z2NtKSArCiAgICBnZW9tX2xpbmUoYWVzKHllYXIsCiAgICAgICAgICAgICAgICAgIHU1bW9ydCwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjb3VudHJ5KSwKICAgICAgICAgICAgICBhbHBoYSA9IDAuMykKcGxvdGx5OjpnZ3Bsb3RseShwKQpgYGAKYGBge3IgdTUtMSwgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOH0KYGBgCgpTb21lIHNlbGVjdGVkIGNvdW50cmllczoKCmBgYHtyIHU1LTIsIGV2YWwgPSBGQUxTRX0KY291bnRyaWVzIDwtIGMoIlVuaXRlZCBTdGF0ZXMiLAogICAgICAgICAgICAgICAiVW5pdGVkIEtpbmdkb20iLAogICAgICAgICAgICAgICAiR2VybWFueSIsCiAgICAgICAgICAgICAgICJDaGluYSIsCiAgICAgICAgICAgICAgICJFZ3lwdCIpCmZpbHRlcih0Z2NtLCBjb3VudHJ5ICVpbiUgY291bnRyaWVzKSAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fbGluZShhZXMoeCA9IHllYXIsCiAgICAgICAgICAgICAgICAgIHkgPSB1NW1vcnQsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gY291bnRyeSkpCmBgYApgYGB7ciB1NS0yLCBlY2hvID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4fQpgYGAKCkV4YW1pbmluZyB0aGUgbWlzc2luZyB2YWx1ZXM6CgpgYGB7ciB1NS0zLCBldmFsID0gRkFMU0V9CnRnY21fbWlzcyA8LQogICAgZ3JvdXBfYnkodGdjbSwgY291bnRyeSkgJT4lCiAgICBzdW1tYXJpemUoYW55TkEgPSBhbnlOQSh1NW1vcnQpKSAlPiUKICAgIGZpbHRlcihhbnlOQSkgJT4lCiAgICBwdWxsKGNvdW50cnkpCgpwIDwtIGZpbHRlcih0Z2NtLAogICAgICAgICAgICBjb3VudHJ5ICVpbiUgdGdjbV9taXNzKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IHllYXIsCiAgICAgICAgICAgICAgIHkgPSB1NW1vcnQsCiAgICAgICAgICAgICAgIGdyb3VwID0gY291bnRyeSkpICsKICAgIGdlb21fbGluZShuYS5ybSA9IFRSVUUpICsKICAgIHhsaW0oYygxOTQwLCAyMDIwKSkKcGxvdGx5OjpnZ3Bsb3RseShwKQpgYGAKYGBge3IgdTUtMywgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gN30KYGBgCg==